home *** CD-ROM | disk | FTP | other *** search
/ ETO Development Tools 2 / ETO Development Tools 2.iso / Tools - Objects / MacApp / MacApp CD Release / MacApp 2.0.1 (Many Libraries) / Examples / DrawShapes / UDrawShapes.inc1.p < prev    next >
Encoding:
Text File  |  1990-10-25  |  75.1 KB  |  3,044 lines  |  [TEXT/MPS ]

  1. {$P}
  2. {[a-,body+,h-,o=100,r+,rec+,t=4,u+,#+,j=20/57/1$,n+]}
  3. { UDrawShapes.inc1.p}
  4. { Copyright © 1985 - 1990 by Apple Computer, Inc.  All rights reserved.}
  5.  
  6. CONST
  7.     kRainbowArrow        = 140;
  8.  
  9.     kPaletteWidth        = 41;                            {Width of the palette}
  10.  
  11.     kMinWidth            = 20;                            {minimum width of new shapes}
  12.     kMinHeight            = 20;                            {minimum height of new shapes}
  13.     { The above two constants define the minimum size a newly-sketched shape
  14.       must become before it is considered a legitimate attempt to draw }
  15.  
  16.     kStaggerAmount        = 16;                            {Amount to stagger windows by}
  17.  
  18.     kColorMenuBar        = 131;                            {Menu bar for a color system}
  19.     kNonColorMenuBar    = 132;                            {Menu bar for a black & white system}
  20.  
  21.     kPickerPrompt        = 256;                            {'STR ' resource for Color Picker}
  22.  
  23.     cChangeShade        = 1012;                         {Buzz command for "Undo Shade Change"}
  24.     cChangeColor        = 1013;                         {Buzz command for "Undo Color Change"}
  25.  
  26. VAR
  27.     gPat:                ARRAY [cWhite..cBlack] OF Pattern;
  28.  
  29.     { prototype shapes for the palette }
  30.  
  31.     gShapesArray:        ARRAY [1..kShapesInPalette] OF TShape;
  32.  
  33.     { bounds of each square in palette }
  34.     gChoiceArray:        ARRAY [0..kShapesInPalette] OF Rect;
  35.  
  36.     gArwBitMap:         BitMap;                         {bitmap used to draw the arrow in palette}
  37.  
  38.     gClipMargin:        Point;                            {the top & left margins to use when
  39.                                                          displaying shapes in the Clipboard}
  40.     gPasteReplacesSelection: BOOLEAN;                    {Tells whether PASTE should REPLACE the
  41.                                                          existing selection, or instead simply add
  42.                                                          new shapes without replacement. Default:
  43.                                                          FALSE; change its value by using the "More
  44.                                                          Debug" menu, obtainable by typing
  45.                                                          command-D}
  46.  
  47.     gConstrainDrags:    BOOLEAN;                        {Whether dragging shapes with the mouse
  48.                                                          should be constrained so that nothing
  49.                                                          overlaps the view's borders. Default:
  50.                                                          TRUE; change its value by using the "More
  51.                                                          Debug" menu, obtainable by typing
  52.                                                          command-D}
  53.  
  54.     gStaggerCount:        INTEGER;                        {For SimpleStagger}
  55.  
  56.     gRainbowArrow:        CCrsrHandle;
  57.  
  58.     gShadeMenu:         TShadeMenu;
  59.  
  60.     gBetterFeedback:    BOOLEAN;                        { TRUE to invoke BetterFeedback routines }
  61.  
  62. {--------------------------------------------------------------------------------------------------}
  63.     {$S AInit}
  64.  
  65. PROCEDURE TShapeApplication.IShapeApplication;            {Initialize the application}
  66.  
  67.     VAR
  68.         r:                    Rect;
  69.         box:                TBox;
  70.         circle:             TCircle;
  71.         hBox:                THeavyBox;
  72.         top:                INTEGER;
  73.         i:                    INTEGER;
  74.         ShadeMenu:            TShadeMenu;
  75.  
  76.     BEGIN
  77.  
  78.     IF qNeedsColorQD | gConfiguration.hasColorQD THEN
  79.         gMBarDisplayed := kColorMenuBar
  80.     ELSE
  81.         gMBarDisplayed := kNonColorMenuBar;
  82.  
  83.     IApplication(kDocType);                             {Generic initialization}
  84.  
  85.     gPat[cWhite] := White;                                {Fill the global array of patterns}
  86.     gPat[cLtGray] := LtGray;
  87.     gPat[cGray] := Gray;
  88.     gPat[cDkGray] := DkGray;
  89.     gPat[cBlack] := Black;
  90.  
  91.     { Install our custom pattern menu }
  92.     New(ShadeMenu);
  93.     FailNil(ShadeMenu);
  94.     ShadeMenu.IShadeMenu;
  95.     gShadeMenu := ShadeMenu;
  96.  
  97.     { Set the standard margins to use in the clipboard }
  98.     SetPt(gClipMargin, 16, 16);
  99.  
  100.     SetRect(r, 10, 50, 28, 70);                         {Define the prototype shapes}
  101.     New(box);
  102.     FailNil(box);
  103.     box.IBox(r, IDBox);
  104.     gShapesArray[IDBox] := box;
  105.  
  106.     OffSetRect(r, 0, 40);
  107.     New(circle);
  108.     FailNil(circle);
  109.     circle.ICircle(r, IDCircle);
  110.     gShapesArray[IDCircle] := circle;
  111.  
  112.     OffSetRect(r, 0, 40);
  113.     New(hBox);
  114.     FailNil(hBox);
  115.     hBox.IHeavyBox(r, IDhBox);
  116.     gShapesArray[IDhBox] := hBox;
  117.  
  118.     WITH gArwBitMap DO                                    {Define the arrow bitmap to be drawn in the
  119.                                                          palette}
  120.         BEGIN
  121.         rowBytes := 2;
  122.         SetRect(bounds, 0, 0, 16, 16);
  123.         baseAddr := @arrow.data;
  124.         END;
  125.  
  126.     top := 0;
  127.     FOR i := 0 TO kShapesInPalette DO                    {Define the palette choices}
  128.         BEGIN
  129.         SetRect(r, 0, top, kPaletteWidth - 1, top + kPaletteWidth - 1);
  130.         gChoiceArray[i] := r;
  131.         top := top + kPaletteWidth - 1;
  132.         END;
  133.  
  134.     gPasteReplacesSelection := FALSE;
  135.     gConstrainDrags := TRUE;
  136.     gStaggerCount := 0;
  137.  
  138.     IF qNeedsColorQD | gConfiguration.hasColorQD THEN
  139.         BEGIN
  140.         { Unlike GetCursor, GetCCursor makes a copy of the color cursor
  141.           resource.  Therefore, you should make one call to GetCCursor
  142.           and multiple calls to SetCCursor }
  143.         gRainbowArrow := GetCCursor(kRainbowArrow);
  144.         FailNil(gRainbowArrow);
  145.         END;
  146.     gBetterFeedback := kBetterFeedbackDesired;
  147.  
  148.     IF qTemplateViews & gDeadStripSuppression THEN
  149.         BEGIN
  150.         IF Member(TObject(NIL), TShapeView) THEN;
  151.         IF Member(TObject(NIL), TPalette) THEN;
  152.         END;
  153.     END;
  154.  
  155. {--------------------------------------------------------------------------------------------------}
  156. {$IFC qDebug}
  157. {$S ASelCommand}
  158.  
  159. FUNCTION TShapeApplication.DoCommandKey(ch: CHAR; VAR info: EventInfo): TCommand; OVERRIDE;
  160.  
  161. { This illustrates how to have a 'Command-key-only' command, i.e. a command
  162.   which is NOT in a menu, but rather only available when the user types
  163.   the 'command' key and another key concurrently.  In this example, the
  164.   user presses 'Command-D' (the D can be in upper or lower case) to
  165.   request that the special 'more debug' menu be put up (or be taken down
  166.   if it was already up) }
  167.  
  168.     BEGIN
  169.     IF ((ch = 'D') | (ch = 'd')) THEN
  170.         DoCommandKey := DoMenuCommand(cCmdDTyped)
  171.     ELSE
  172.         DoCommandKey := INHERITED DoCommandKey(ch, info);
  173.     END;
  174. {$ENDC}
  175.  
  176. {--------------------------------------------------------------------------------------------------}
  177. {$S AOpen}
  178.  
  179. FUNCTION TShapeApplication.DoMakeDocument(itsCmdNumber: CmdNumber): TDocument;
  180. { NB: Not used to create the document for a shape view in the Clipboard }
  181.  
  182.     VAR
  183.         shapeDocument:        TShapeDocument;
  184.  
  185.     BEGIN
  186.     New(shapeDocument);
  187.     FailNil(shapeDocument);
  188.     shapeDocument.IShapeDocument(kDocType);
  189.     DoMakeDocument := shapeDocument;
  190.     END;
  191.  
  192. {--------------------------------------------------------------------------------------------------}
  193. {$IFC qDebug}
  194. {$S ASelCommand}
  195.  
  196. FUNCTION TShapeApplication.DoMenuCommand(aCmdNumber: CmdNumber): TCommand; OVERRIDE;
  197.  
  198.     BEGIN
  199.     DoMenuCommand := NIL;
  200.     CASE aCmdNumber OF
  201.  
  202.         cPasteReplacesSelection:
  203.             gPasteReplacesSelection := NOT gPasteReplacesSelection;
  204.  
  205.         cConstrainDrags:
  206.             gConstrainDrags := NOT gConstrainDrags;
  207.  
  208.         cCmdDTyped:                                     {Command-D typed by user}
  209.             BEGIN
  210.             IF GetMHandle(mMoreDebug) = NIL THEN        {menu not currrently up--put it up}
  211.                 InsertMenu(GetResMenu(mMoreDebug), 0)
  212.             ELSE                                        {menu currently up -- take it down}
  213.                 DeleteMenu(mMoreDebug);
  214.             InvalidateMenuBar;                            { Get it redrawn }
  215.             END;
  216.  
  217.         OTHERWISE
  218.             DoMenuCommand := INHERITED DoMenuCommand(aCmdNumber);
  219.     END;                                                {Case}
  220.     END;
  221. {$ENDC}
  222.  
  223. {--------------------------------------------------------------------------------------------------}
  224. {$IFC qDebug}
  225. {$S ARes}
  226.  
  227. PROCEDURE TShapeApplication.DoSetupMenus; OVERRIDE;
  228. {The only menu commands handled here are the following two debugging commands:}
  229.  
  230.     BEGIN
  231.     INHERITED DoSetupMenus;
  232.     EnableCheck(cPasteReplacesSelection, TRUE, gPasteReplacesSelection);
  233.     EnableCheck(cConstrainDrags, TRUE, gConstrainDrags);
  234.     END;
  235. {$ENDC}
  236.  
  237. {--------------------------------------------------------------------------------------------------}
  238. {$IFC qDebug}
  239. {$S ADebug}
  240.  
  241. PROCEDURE TShapeApplication.IdentifySoftware; OVERRIDE;
  242.  
  243.     BEGIN
  244.     WriteLn('DrawShapes Source date: 6 June 86; Compiled on: ', COMPDATE, ' @ ', COMPTIME);
  245.     INHERITED IdentifySoftware;
  246.     END;
  247. {$ENDC}
  248.  
  249. {--------------------------------------------------------------------------------------------------}
  250. {$S AClipboard}
  251.  
  252. FUNCTION TShapeApplication.MakeViewForAlienClipboard: TView; OVERRIDE;
  253. { Launch a view to represent the data found in the Clipboard at
  254.   application start-up time, or when returning from an excursion
  255.   to Switcher, or when returning from a Desk Accessory }
  256.  
  257.     VAR
  258.         offset:             LONGINT;
  259.         clipShapeView:        TShapeView;
  260.         clipShapeDoc:        TShapeDocument;
  261.         clipShapes:         ShapesOnClipboard;
  262.         aNewShape:            TShape;
  263.         i:                    INTEGER;
  264.         err:                LONGINT;
  265.         perm:                BOOLEAN;
  266.         fi:                 FailInfo;
  267.  
  268.     PROCEDURE HdlFailure(error: OSErr; message: LONGINT);
  269.  
  270.         BEGIN
  271.         Handle(clipShapes) := DisposeIfHandle(clipShapes);
  272.  
  273.         FreeIfObject(clipShapeDoc);
  274.         clipShapeDoc := NIL;
  275.         END;
  276.  
  277.     BEGIN
  278.     clipShapes := NIL;
  279.  
  280.     {Before doing anything else, make sure the scrap contains shapes}
  281.     IF GetScrap(NIL, kShapeClipType, offset) > 0 THEN    {found my kind of data }
  282.         BEGIN
  283.         New(clipShapeDoc);
  284.         FailNil(clipShapeDoc);
  285.         clipShapeDoc.IShapeDocument(kDocType);
  286.  
  287.         CatchFailures(fi, HdlFailure);
  288.         New(clipShapeView);
  289.         FailNil(clipShapeView);
  290.         clipShapeView.IShapeView(clipShapeDoc, NIL, TRUE);
  291.  
  292.         clipShapeDoc.fShapeView := clipShapeView;
  293.  
  294.         clipShapes := ShapesOnClipboard(NewPermHandle(0));
  295.         FailNil(clipShapes);
  296.         FailSpaceIsLow;
  297.  
  298.         perm := PermAllocation(TRUE);                    {Don't allow GetScrap to use temp space}
  299.         err := GetScrap(Handle(clipShapes), kShapeClipType, offset);
  300.         perm := PermAllocation(perm);                    {Restore perm allocation setting}
  301.  
  302.   { Only a negative result indicates an error--FailOSErr considers
  303.     any non-zero result an error.}
  304.         IF err < 0 THEN
  305.             FailOSErr(err);
  306.  
  307.         FOR i := 0 TO clipShapes^^.theNumberOfShapes - 1 DO
  308.             BEGIN
  309.             aNewShape := TShape(gShapesArray[clipShapes^^.theShapes[i].theId].Clone);
  310.             FailNil(aNewShape);
  311.             WITH aNewShape, clipShapes^^.theShapes[i] DO
  312.                 BEGIN
  313.                 fShade := theShade;
  314.                 fColor := theColor;
  315.                 fExtentRect := theRect;
  316.                 END;
  317.             clipShapeDoc.AddShape(aNewShape);
  318.             END;
  319.  
  320.         Success(fi);
  321.         Handle(clipShapes) := DisposeIfHandle(clipShapes);
  322.  
  323.         MakeViewForAlienClipboard := clipShapeView;
  324.         END
  325.     ELSE
  326.         MakeViewForAlienClipboard := INHERITED MakeViewForAlienClipboard;
  327.     END;
  328.  
  329. {--------------------------------------------------------------------------------------------------}
  330. {$S AOpen}
  331.  
  332. PROCEDURE TShapeDocument.IShapeDocument(fileType: OSType);
  333.  
  334.     VAR
  335.         fi:                 FailInfo;
  336.  
  337.     PROCEDURE HdlNewList(error: OSErr; message: LONGINT);
  338.  
  339.         BEGIN
  340.         Free;
  341.         END;
  342.  
  343.     BEGIN
  344.     fShapeView := NIL;
  345.     fPaletteView := NIL;
  346.     fShapeList := NIL;                                    {Just in case IDocument fails}
  347.     IDocument(fileType, kDocType, kUsesDataFork, kUsesRsrcFork, NOT kDataOpen, NOT kRsrcOpen);
  348.  
  349.     CatchFailures(fi, HdlNewList);                        { In case NewList fails.}
  350.     fShapeList := NewList;
  351.     Success(fi);
  352.  
  353.     {$IFC qDebug}
  354.     fShapeList.SetEltType('TShape');
  355.     {$ENDC}
  356.  
  357.     fSavePrintInfo := TRUE;
  358.  
  359.     fReopening := FALSE;
  360.     fFiltering := FALSE;
  361.     fReplaceCommand := NIL;
  362.     END;
  363.  
  364. {--------------------------------------------------------------------------------------------------}
  365. {$S ARes}
  366.  
  367. PROCEDURE TShapeDocument.AddShape(shape: TShape);
  368.  
  369.     BEGIN
  370.     fShapeList.InsertLast(shape);
  371.     END;
  372.  
  373. {--------------------------------------------------------------------------------------------------}
  374. {$S ARes}
  375.  
  376. PROCEDURE TShapeDocument.DeleteShape(shape: TShape);
  377. { Doesn't work for shape still belonging to a command
  378.   (i.e., not yet committed to the document }
  379.  
  380.     BEGIN
  381.     fShapeList.Delete(shape);
  382.     FreeIfObject(shape);
  383.     shape := NIL;
  384.     END;
  385.  
  386. {--------------------------------------------------------------------------------------------------}
  387. {$S AOpen}
  388.  
  389. PROCEDURE TShapeDocument.DoMakeViews(forPrinting: BOOLEAN);
  390.  
  391.     VAR
  392.         shapeView:            TShapeView;
  393.         palette:            TPalette;
  394.         aWindow:            TWindow;
  395.         aDocState:            DocState;
  396.         minSize:            Point;
  397.         maxSize:            Point;
  398.  
  399.     PROCEDURE CreateProceduralShapeView;
  400.         { CreateProceduralShapeView used when creating views procedurally to create the shapes view
  401.         in both printing & non-printing cases }
  402.  
  403.         BEGIN
  404.         New(shapeView);
  405.         FailNil(shapeView);
  406.         shapeView.IShapeView(SELF, palette, FALSE);
  407.         
  408.         fShapeView := shapeView;
  409.         END;
  410.  
  411.     PROCEDURE RestoreWindow;
  412.         { RestoreWindow restores the window & scroller using the settings in the documents fDocState
  413.         field }
  414.  
  415.         BEGIN
  416.         aDocState := fDocState;
  417.         WITH aDocState.theWindowRect DO
  418.             BEGIN
  419.             aWindow.Resize(right - left, bottom - top, FALSE);
  420.             aWindow.Locate(left, top, FALSE);
  421.             END;
  422.         aWindow.ForceOnScreen;
  423.         WITH aDocState.theScrollPosition DO
  424.             fShapeView.fScroller.ScrollTo(h, v, FALSE);
  425.         END;
  426.  
  427.     BEGIN
  428.     IF forPrinting THEN
  429.         BEGIN
  430.         IF qTemplateViews THEN
  431.             BEGIN
  432.             shapeView := TShapeView(DoCreateViews(SELF, NIL, kShapeViewRSRCID, gZeroVPt));
  433.             fShapeView := shapeView;
  434.             END
  435.         ELSE
  436.             BEGIN
  437.             palette := NIL;
  438.             CreateProceduralShapeView;
  439.             END;
  440.         END                                             { this is the end of the "forPrinting=TRUE"
  441.                                                          case }
  442.     ELSE
  443.         BEGIN
  444.         IF qTemplateViews THEN
  445.             BEGIN
  446.             aWindow := NewTemplateWindow(kShapeWindowRSRCID, SELF);
  447.             FailNil(aWindow);
  448.  
  449.             fPaletteView := TPalette(aWindow.FindSubView('PLTT'));
  450.             FailNil(fPaletteView);
  451.  
  452.             fShapeView := TShapeView(aWindow.FindSubView('SHAP'));
  453.             FailNil(fShapeView);
  454.  
  455.             fShapeView.fPalette := fPaletteView;
  456.             END
  457.         ELSE
  458.             BEGIN
  459.             New(palette);
  460.             FailNil(palette);
  461.  
  462.             palette.IPalette(SELF);
  463.             fPaletteView := palette;
  464.  
  465.             CreateProceduralShapeView;
  466.  
  467.             aWindow := NewPaletteWindow(kShapeWindowRSRCID, kWantHScrollBar, kWantVScrollBar, SELF,
  468.                                         fShapeView, fPaletteView, kPaletteWidth, kLeftPalette);
  469.             END;
  470.  
  471.         fShapeView.fScroller := fShapeView.GetScroller(TRUE);
  472.  
  473.         IF fReopening THEN
  474.             RestoreWindow
  475.         ELSE
  476.             BEGIN
  477.             aWindow.AdaptToScreen;
  478.             aWindow.SimpleStagger(kStaggerAmount, kStaggerAmount, gStaggerCount);
  479.             END;
  480.  
  481.         { set window's resize limits so it can't become wider than the shapeview's edge }
  482.         WITH aWindow.fResizeLimits DO
  483.             BEGIN
  484.             minSize := topLeft;
  485.             maxSize := botRight;
  486.             END;
  487.         WITH maxSize DO
  488.             h := Min(fShapeView.fSize.h + fPaletteView.fSize.h + kSBarSizeMinus1, h);
  489.         aWindow.SetResizeLimits(minSize, maxSize);
  490.         END;
  491.     END;
  492.  
  493. {--------------------------------------------------------------------------------------------------}
  494. {$S AWriteFile}
  495.  
  496. PROCEDURE TShapeDocument.DoNeedDiskSpace(VAR dataForkBytes, rsrcForkBytes: LONGINT);
  497.  
  498.     BEGIN
  499.     { get Print record requirements }
  500.     INHERITED DoNeedDiskSpace(dataForkBytes, rsrcForkBytes);
  501.  
  502.     dataForkBytes := dataForkBytes + fShapeList.GetSize * SIZEOF(ShapeData);
  503.     rsrcForkBytes := rsrcForkBytes + kRsrcTypeOverhead + kRsrcOverhead + SIZEOF(DocState);
  504.     END;
  505.  
  506. {
  507. Doc file has the following format:
  508.   Data Fork:
  509.     (a)  kSizePrintInfo (120 bytes) => PrintInfo
  510.     (b)  The rest => The shapes themselves
  511.  
  512.  Resource Fork:
  513.     (a)  SIZEOF(DocState) => DocState (rsrc type: 'DSTA', number: 1)
  514. }
  515.  
  516. {--------------------------------------------------------------------------------------------------}
  517. {$S AReadFile}
  518.  
  519. PROCEDURE TShapeDocument.DoRead(aRefNum: INTEGER; rsrcExists, forPrinting: BOOLEAN);
  520.  
  521.     VAR
  522.         i:                    INTEGER;
  523.         id:                 INTEGER;
  524.         count:                LONGINT;
  525.         newShape:            TShape;
  526.         docStateHandle:     HDocState;
  527.  
  528.     BEGIN
  529.     INHERITED DoRead(aRefNum, rsrcExists, forPrinting); {read print info stuff}
  530.  
  531.     IF rsrcExists THEN
  532.         BEGIN
  533.         docStateHandle := HDocState(GetResource(kDocRsrcKind, kDocStateID));
  534.         IF docStateHandle <> NIL THEN
  535.             BEGIN
  536.             fDocState := docStateHandle^^;
  537.             fReopening := TRUE;
  538.             END;
  539.         END
  540.     ELSE
  541.         BEGIN
  542.         {$IFC qDebug}
  543.         ProgramBreak('Resource fork doesn''t exist for saved file');
  544.         {$ENDC}
  545.         Failure(1                                        {???} , 0);
  546.         END;
  547.  
  548.     FOR i := 1 TO fDocState.theNumberOfShapes DO
  549.         BEGIN
  550.         count := SIZEOF(INTEGER);
  551.         FailOSErr(FSRead(aRefNum, count, @id));
  552.  
  553.         IF (id >= 1) & (id <= kShapesInPalette) THEN
  554.             BEGIN
  555.             newShape := TShape(gShapesArray[id].Clone);
  556.             FailNil(newShape);
  557.  
  558.             newShape.ReadFrom(aRefNum);
  559.  
  560.             AddShape(newShape);
  561.             END
  562.             {$IFC qDebug}
  563.         ELSE
  564.             WriteLn('Ignored invalid shape ID = ', id: 1, ' shape #: ', i: 1)
  565.             {$ENDC} ;
  566.         END;
  567.     END;
  568.  
  569. {--------------------------------------------------------------------------------------------------}
  570. {$S AWriteFile}
  571.  
  572. PROCEDURE TShapeDocument.DoWrite(aRefNum: INTEGER; makingCopy: BOOLEAN);
  573.  
  574.     VAR
  575.         vhs:                VHSelect;
  576.         aDocState:            DocState;
  577.         count:                LONGINT;
  578.         window:             TWindow;
  579.         numberOfShapes:     INTEGER;
  580.         dummyRect:            Rect;
  581.         docStateHandle:     HDocState;
  582.         aWindowRect:        Rect;
  583.  
  584. {--------------------------------------------------------------------------------------------------}
  585.  
  586.     PROCEDURE WriteShape(shape: TShape);
  587.  
  588.         BEGIN
  589.         shape.WriteTo(aRefNum);
  590.         END;
  591.  
  592.     BEGIN
  593.     INHERITED DoWrite(aRefNum, makingCopy);             {write print info stuff}
  594.  
  595.     { Call SurveyShapes just to get number of shapes }
  596.     SurveyShapes(FALSE, numberOfShapes, dummyRect);
  597.     window := TWindow(fWindowList.First);
  598.  
  599.     docStateHandle := HDocState(NewHandle(SIZEOF(DocState)));
  600.     FailNil(docStateHandle);
  601.  
  602.     window.GetGlobalBounds(aWindowRect);
  603.     docStateHandle^^.theWindowRect := aWindowRect;
  604.     docStateHandle^^.theScrollPosition := fShapeView.fScroller.fTranslation;
  605.     docStateHandle^^.theNumberOfShapes := numberOfShapes;
  606.  
  607.     AddResource(Handle(docStateHandle), kDocRsrcKind, kDocStateID, 'Doc State');
  608.     FailOSErr(ResError);
  609.  
  610.     EachVirtualShapeDo(WriteShape);
  611.     END;
  612.  
  613. {--------------------------------------------------------------------------------------------------}
  614. {$S ARes}
  615.  
  616. PROCEDURE TShapeDocument.EachShapeDo(PROCEDURE DoThis(shape: TShape));
  617. { Iterates through the list of shapes. We have a separate method for this
  618.   to hide the actual implementation of the shape list structure. }
  619.  
  620.     BEGIN
  621.     fShapeList.Each(DoThis);
  622.     END;
  623.  
  624. {--------------------------------------------------------------------------------------------------}
  625. {$S ARes}
  626.  
  627. PROCEDURE TShapeDocument.EachPotentialShapeDo(PROCEDURE
  628.                                               DoThis(shape: TShape));
  629. { Iterates through all the shapes in document plus any 'pastee' shapes
  630.   which may have been added by a not-yet-committed Paste.}
  631.  
  632.     BEGIN
  633.     EachShapeDo(DoThis);
  634.     IF fReplaceCommand <> NIL THEN
  635.         fReplaceCommand.EachNewShapeDo(DoThis);
  636.     END;
  637.  
  638. {--------------------------------------------------------------------------------------------------}
  639. {$S ARes}
  640.  
  641. PROCEDURE TShapeDocument.EachVirtualShapeDo(PROCEDURE
  642.                                             DoThis(shape: TShape));
  643. { EachVirtualShape iterates through only those shapes that appear
  644.   to be present at the moment to the USER, given the
  645.   UNDO/REDO status of the last command. Thus it iterates
  646.   through some but possibly not all of the shapes in the
  647.   document, and possibly also through not-yet-in-the-document pastees }
  648.  
  649. {--------------------------------------------------------------------------------------------------}
  650.  
  651.     PROCEDURE MaybeDoThis(shape: TShape);
  652.  
  653.         BEGIN
  654.         IF (NOT fFiltering) | (NOT shape.fWasSelected) THEN
  655.             DoThis(shape);
  656.         END;
  657.  
  658.     BEGIN
  659.     EachShapeDo(MaybeDoThis);
  660.     IF fReplaceCommand <> NIL THEN
  661.         fReplaceCommand.EachNewShapeDo(DoThis);
  662.     END;
  663.  
  664. {--------------------------------------------------------------------------------------------------}
  665. {$S AFields}
  666.  
  667. PROCEDURE TShapeDocument.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  668.                                                     fieldType: INTEGER)); OVERRIDE;
  669.  
  670.     BEGIN
  671.     DoToField('TShapeDocument', NIL, bClass);
  672.     DoToField('fShapeView', @fShapeView, bObject);
  673.     DoToField('fPaletteView', @fPaletteView, bObject);
  674.     DoToField('fShapeList', @fShapeList, bObject);
  675.     DoToField('fDocState.theNumberOfShapes', @fDocState.theNumberOfShapes, bInteger);
  676.     DoToField('fDocState.theWindowRect', @fDocState.theWindowRect, bRect);
  677.     DoToField('fDocState.theScrollPosition', @fDocState.theScrollPosition, bPoint);
  678.     DoToField('fReopening', @fReopening, bBoolean);
  679.     DoToField('fReplaceCommand', @fReplaceCommand, bObject);
  680.     DoToField('fFiltering', @fFiltering, bBoolean);
  681.     INHERITED Fields(DoToField);
  682.     END;
  683.  
  684. {--------------------------------------------------------------------------------------------------}
  685. {$S ARes}
  686.  
  687. FUNCTION TShapeDocument.FirstSelectedShapeThat(FUNCTION
  688.                                                TestSelectedShape(aShape: TShape): BOOLEAN): TShape;
  689.  
  690.     VAR
  691.         aShape:             TShape;
  692.  
  693.     FUNCTION TestShape(aShape: TObject): BOOLEAN;
  694.  
  695.         BEGIN
  696.         IF TShape(aShape).fIsSelected THEN
  697.             TestShape := TestSelectedShape(TShape(aShape))
  698.         ELSE
  699.             TestShape := FALSE;
  700.         END;
  701.  
  702.     BEGIN
  703.     aShape := TShape(fShapeList.FirstThat(TestShape));
  704.     IF (aShape = NIL) & (fReplaceCommand <> NIL) THEN
  705.         aShape := fReplaceCommand.FirstShapeThat(TestSelectedShape);
  706.     FirstSelectedShapeThat := aShape;
  707.     END;
  708.  
  709. {--------------------------------------------------------------------------------------------------}
  710. {$S AClose}
  711.  
  712. PROCEDURE TShapeDocument.Free; OVERRIDE;
  713.  
  714.     BEGIN
  715.     FreeData;
  716.     FreeIfObject(fShapeList);
  717.     fShapeList := NIL;
  718.  
  719.     INHERITED Free;
  720.     END;
  721.  
  722. {--------------------------------------------------------------------------------------------------}
  723. {$S AClose}
  724.  
  725. PROCEDURE TShapeDocument.FreeData;
  726.  
  727.     PROCEDURE DoToShape(aShape: TShape);
  728.  
  729.         BEGIN
  730.         FreeIfObject(aShape);
  731.         END;
  732.  
  733.     BEGIN
  734.     IF fShapeList <> NIL THEN
  735.         BEGIN
  736.         EachShapeDo(DoToShape);
  737.         fShapeList.DeleteAll;
  738.         END;
  739.     END;
  740.  
  741. {--------------------------------------------------------------------------------------------------}
  742. {$S ARes}
  743.  
  744. PROCEDURE TShapeDocument.SurveyShapes(selecteesOnly: BOOLEAN; VAR numberOfShapes: INTEGER;
  745.                                       VAR combinedExtent: Rect);
  746.  
  747.     PROCEDURE UnionizeShapes(shape: TShape);
  748.  
  749.         BEGIN
  750.         IF shape.fIsSelected | NOT selecteesOnly THEN
  751.             BEGIN
  752.             numberOfShapes := numberOfShapes + 1;
  753.             IF numberOfShapes > 1 THEN
  754.             {$Push} {$h-}
  755.                 UnionRect(shape.fExtentRect, combinedExtent, combinedExtent)
  756.                 {$Pop}
  757.  
  758.             ELSE
  759.                 combinedExtent := shape.fExtentRect;
  760.             END;
  761.         END;
  762.  
  763.     BEGIN
  764.     numberOfShapes := 0;
  765.     combinedExtent := gZeroRect;
  766.     EachVirtualShapeDo(UnionizeShapes);
  767.     END;
  768.  
  769. {--------------------------------------------------------------------------------------------------}
  770. {$S AOpen}
  771.  
  772. PROCEDURE TPalette.IPalette(itsDocument: TDocument);
  773.  
  774.     VAR
  775.         itsSize:            VPoint;
  776.  
  777.     BEGIN
  778.     SetVPt(itsSize, kPaletteWidth, 0);
  779.     IView(itsDocument, NIL, gZeroVPt, itsSize, sizeFixed, sizeSuperView);
  780.  
  781.     fCurrShape := 0;
  782.     END;
  783.  
  784. {--------------------------------------------------------------------------------------------------}
  785. {$IFC qTemplateViews}
  786. {$S AOpen}
  787.  
  788. PROCEDURE TPalette.IRes(itsDocument: TDocument; itsSuperview: TView; VAR itsParams: Ptr); OVERRIDE;
  789.  
  790.     BEGIN
  791.     INHERITED IRes(itsDocument, itsSuperview, itsParams);
  792.  
  793.     fCurrShape := 0;
  794.     END;
  795.  
  796. {$ENDC}
  797.  
  798. {--------------------------------------------------------------------------------------------------}
  799. {$S ARes}
  800.  
  801. PROCEDURE TPalette.DoHighlightSelection(fromHL, toHL: HLState);
  802.  
  803.     VAR
  804.         r:                    Rect;
  805.  
  806.     BEGIN
  807.     IF (fromHL <> toHL) & (fromHL + toHL <> hlOffDim) THEN
  808.         BEGIN
  809.         r := gChoiceArray[fCurrShape];
  810.         UseSelectionColor;
  811.         InvertRect(r);
  812.         END;
  813.     END;
  814.  
  815. {--------------------------------------------------------------------------------------------------}
  816. {$S ASelCommand}
  817.  
  818. FUNCTION TPalette.DoMouseCommand(VAR theMouse: Point; VAR info: EventInfo;
  819.                                  VAR hysteresis: Point): TCommand;
  820.  
  821.     VAR
  822.         i:                    INTEGER;
  823.  
  824.     BEGIN
  825.     DoMouseCommand := NIL;
  826.  
  827.     i := 0;
  828.     REPEAT
  829.         IF PtInRect(theMouse, gChoiceArray[i]) THEN
  830.             LEAVE;
  831.         i := i + 1;
  832.     UNTIL i > kShapesInPalette;
  833.  
  834.     IF (i <= kShapesInPalette) & (i <> fCurrShape) THEN
  835.         BEGIN
  836.         IF Focus THEN
  837.             DoHighlightSelection(hlOn, hlOff);
  838.         fCurrShape := i;
  839.         IF Focus THEN
  840.             DoHighlightSelection(hlOff, hlOn);
  841.         END;
  842.     END;
  843.  
  844. {--------------------------------------------------------------------------------------------------}
  845. {$S ARes}
  846.  
  847. FUNCTION TPalette.DoSetCursor(localPoint: Point; cursorRgn: RgnHandle): BOOLEAN; OVERRIDE;
  848.  
  849.     VAR
  850.         qdExtent:            Rect;
  851.  
  852.     BEGIN
  853.     SetCursor(arrow);
  854.     GetQDExtent(qdExtent);
  855.     RectRgn(cursorRgn, qdExtent);
  856.     DoSetCursor := TRUE;
  857.     END;
  858.  
  859. {--------------------------------------------------------------------------------------------------}
  860. {$S ARes}
  861.  
  862. PROCEDURE TPalette.Draw(area: Rect);
  863.  
  864.     VAR
  865.         i:                    INTEGER;
  866.         r:                    Rect;
  867.  
  868.     BEGIN
  869.     PenSize(1, 1);
  870.     MoveTo(fSize.h - 1, 0);
  871.     Line(0, fSize.v);
  872.     FOR i := 0 TO kShapesInPalette DO
  873.         BEGIN
  874.         FrameRect(gChoiceArray[i]);
  875.  
  876.         IF i = 0 THEN
  877.             BEGIN
  878.             SetRect(r, 14, 12, 30, 28);
  879.             CopyBits(gArwBitMap, thePort^.portBits, gArwBitMap.bounds, r, srcOR, NIL);
  880.             END
  881.         ELSE
  882.             gShapesArray[i].Draw;
  883.         END;
  884.  
  885.     INHERITED Draw(area);
  886.     END;
  887.  
  888. {--------------------------------------------------------------------------------------------------}
  889. {$S AFields}
  890.  
  891. PROCEDURE TPalette.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  892.                           fieldType: INTEGER)); OVERRIDE;
  893.  
  894.     BEGIN
  895.     DoToField('TPalette', NIL, bClass);
  896.     DoToField('fCurrShape', @fCurrShape, bInteger);
  897.     INHERITED Fields(DoToField);
  898.     END;
  899.  
  900. {--------------------------------------------------------------------------------------------------}
  901. {$S AOpen}
  902.  
  903. PROCEDURE TShapeView.IShapeView(itsDocument: TShapeDocument; itsPalette: TPalette;
  904.                                 forClipboard: BOOLEAN);
  905.  
  906.     VAR
  907.         itsLocation:        VPoint;
  908.         itsSize:            VPoint;
  909.         aHandler:            TStdPrintHandler;
  910.         aDocState:            DocState;
  911.         sd:                 SizeDeterminer;
  912.  
  913.     BEGIN
  914.     fDragging := FALSE;
  915.     fPalette := itsPalette;
  916.     SetVPt(itsSize, kMaxCoord, kMaxCoord);
  917.     IF forClipboard THEN
  918.         sd := sizeVariable
  919.     ELSE
  920.         sd := sizeFillPages;
  921.     IView(itsDocument, NIL, gZeroVPt, itsSize, sd, sd);
  922.     fScroller := NIL;
  923.  
  924.     fShapeDocument := itsDocument;
  925.  
  926.     {$IFC FALSE}                                        {!!! Need to handle this}
  927.     IF forClipboard THEN
  928.         fWouldMakePICTScrap := TRUE;
  929.     {$ENDC}
  930.  
  931.     IF NOT forClipboard THEN
  932.         BEGIN
  933.         New(aHandler);
  934.         FailNil(aHandler);
  935.         aHandler.IStdPrintHandler(itsDocument, SELF, NOT kSquareDots, { does not have square dots }
  936.                                   kFixedSize,            { horizontal page size is fixed }
  937.                                   kFixedSize);            { vertical page size is fixed }
  938.         END;
  939.  
  940.     fClickPt := gZeroPt;                                {plausible starting value}
  941.     END;
  942.  
  943. {--------------------------------------------------------------------------------------------------}
  944. {$IFC qTemplateViews}
  945. {$S AOpen}
  946.  
  947. PROCEDURE TShapeView.IRes(itsDocument: TDocument; itsSuperview: TView;
  948.     VAR itsParams: Ptr); OVERRIDE;
  949.  
  950.     VAR
  951.         aHandler:            TStdPrintHandler;
  952.  
  953.     BEGIN
  954.     fDragging := FALSE;
  955.     fScroller := NIL;
  956.     fPalette := NIL;
  957.  
  958.     INHERITED IRes(itsDocument, itsSuperview, itsParams);
  959.  
  960.     fShapeDocument := TShapeDocument(itsDocument);
  961.  
  962.     New(aHandler);
  963.     FailNil(aHandler);
  964.     aHandler.IStdPrintHandler(itsDocument, SELF, NOT kSquareDots, { does not have square dots }
  965.                               kFixedSize,                { horizontal page size is fixed }
  966.                               kFixedSize);                { vertical page size is fixed }
  967.  
  968.     fClickPt := gZeroPt;                                {plausible starting value}
  969.     END;
  970.  
  971. {$ENDC}
  972.  
  973. {--------------------------------------------------------------------------------------------------}
  974. {$S ARes}
  975.  
  976. PROCEDURE TShapeView.CalcMinSize(VAR minSize: VPoint);
  977.  
  978.     VAR
  979.         aRect:                Rect;
  980.         numberOfShapes:     INTEGER;
  981.  
  982.     BEGIN
  983.     fShapeDocument.SurveyShapes(FALSE, numberOfShapes, aRect);
  984.     SetVPt(minSize, Max(100, aRect.right), Max(100, aRect.bottom));
  985.     END;
  986.  
  987. {--------------------------------------------------------------------------------------------------}
  988. {$S ARes}
  989.  
  990. FUNCTION TShapeView.ContainsClipType(aType: ResType): BOOLEAN;
  991.  
  992.     BEGIN
  993.     ContainsClipType := (aType = kShapeClipType);
  994.     END;
  995.  
  996. {--------------------------------------------------------------------------------------------------}
  997. {$S ARes}
  998.  
  999. PROCEDURE TShapeView.Deselect;
  1000.  
  1001.     PROCEDURE DeselShape(shape: TShape);
  1002.  
  1003.         BEGIN
  1004.         shape.fIsSelected := FALSE;
  1005.         END;
  1006.  
  1007.     BEGIN
  1008.     DoHighlightSelection(hlOn, hlOff);
  1009.     fShapeDocument.EachPotentialShapeDo(DeselShape);
  1010.     END;
  1011.  
  1012. {--------------------------------------------------------------------------------------------------}
  1013. {$S ARes}
  1014.  
  1015. PROCEDURE TShapeView.DoHighlightSelection(fromHL, toHL: HLState);
  1016.  
  1017.     PROCEDURE HiliteShape(shape: TShape);
  1018.  
  1019.         BEGIN
  1020.         IF shape.fIsSelected & (NOT fDragging) THEN
  1021.             shape.Highlight(fromHL, toHL);
  1022.         END;
  1023.  
  1024.     BEGIN
  1025.     fShapeDocument.EachVirtualShapeDo(HiliteShape);
  1026.     END;
  1027.  
  1028. {--------------------------------------------------------------------------------------------------}
  1029. {$S ASelCommand}
  1030.  
  1031. FUNCTION TShapeView.DoMenuCommand(aCmdNumber: CmdNumber): TCommand;
  1032.  
  1033.     VAR
  1034.         recolorCmd:         TRecolorCmd;
  1035.         reshadeCmd:         TReshadeCmd;
  1036.         shapeCutCopyCommand: TShapeCutCopyCommand;
  1037.         shapePasteCommand:    TShapePasteCommand;
  1038.         shapeClearCommand:    TShapeClearCommand;
  1039.         menu, item:         INTEGER;
  1040.         pMCEntry:            MCEntryPtr;
  1041.         theColor:            RGBColor;
  1042.         pickerPrompt:        StringHandle;
  1043.  
  1044.     PROCEDURE SelectIt(aShape: TShape);
  1045.  
  1046.         BEGIN
  1047.         IF NOT aShape.fIsSelected THEN
  1048.             BEGIN
  1049.             aShape.fIsSelected := TRUE;
  1050.             aShape.Highlight(hlOff, hlOn);
  1051.             END;
  1052.         END;
  1053.  
  1054.     FUNCTION GetShapeColor(aShape: TShape): BOOLEAN;
  1055.  
  1056.         BEGIN
  1057.         GetShapeColor := TRUE;
  1058.         END;
  1059.  
  1060.     BEGIN
  1061.     DoMenuCommand := NIL;
  1062.  
  1063.     CASE aCmdNumber OF
  1064.  
  1065.         cWhite, cLtGray, cGray, cDkGray, cBlack:
  1066.             BEGIN
  1067.             New(reshadeCmd);
  1068.             FailNil(reshadeCmd);
  1069.             reshadeCmd.IReshadeCmd(aCmdNumber, SELF);
  1070.             DoMenuCommand := reshadeCmd;
  1071.             END;
  1072.  
  1073.         cCut, cCopy:
  1074.             BEGIN
  1075.             New(shapeCutCopyCommand);
  1076.             FailNil(shapeCutCopyCommand);
  1077.             shapeCutCopyCommand.IShapeCutCopyCommand(aCmdNumber, SELF);
  1078.             DoMenuCommand := shapeCutCopyCommand;
  1079.             END;
  1080.  
  1081.         cPaste:
  1082.             BEGIN
  1083.             New(shapePasteCommand);
  1084.             FailNil(shapePasteCommand);
  1085.             shapePasteCommand.IShapePasteCommand(SELF);
  1086.             DoMenuCommand := shapePasteCommand;
  1087.             END;
  1088.  
  1089.         cClear:
  1090.             BEGIN
  1091.             New(shapeClearCommand);
  1092.             FailNil(shapeClearCommand);
  1093.             shapeClearCommand.IShapeClearCommand(SELF);
  1094.             DoMenuCommand := shapeClearCommand;
  1095.             END;
  1096.  
  1097.         {$IFC qDebug}
  1098.         cRecalcExtent:
  1099.             AdjustSize;
  1100.         {$ENDC}
  1101.  
  1102.         cSelectAll:
  1103.             BEGIN
  1104.             IF Focus THEN;                                {At least try to focus}
  1105.             fShapeDocument.EachVirtualShapeDo(SelectIt);
  1106.             END;
  1107.  
  1108.         cPickColor:
  1109.             BEGIN
  1110.             theColor := fShapeDocument.FirstSelectedShapeThat(GetShapeColor).fColor;
  1111.             pickerPrompt := GetString(kPickerPrompt);
  1112.             FailNil(pickerPrompt);
  1113.             IF GetColor(gZeroPt, pickerPrompt^^, theColor, theColor) THEN
  1114.                 BEGIN
  1115.                 New(recolorCmd);
  1116.                 FailNil(recolorCmd);
  1117.                 recolorCmd.IRecolorCmd(theColor, SELF);
  1118.                 DoMenuCommand := recolorCmd;
  1119.                 END;
  1120.             END;
  1121.  
  1122.         cBetterFeedback:
  1123.             gBetterFeedback := NOT gBetterFeedback;
  1124.  
  1125.         OTHERWISE
  1126.             BEGIN
  1127.             CmdToMenuItem(aCmdNumber, menu, item);
  1128.             IF menu = mColor THEN
  1129.                 BEGIN
  1130.                 New(recolorCmd);
  1131.                 FailNil(recolorCmd);
  1132.                 pMCEntry := GetMCEntry(menu, item);
  1133.                 theColor := pMCEntry^.mctRGB2;            {the MC entry can move}
  1134.                 recolorCmd.IRecolorCmd(theColor, SELF);
  1135.                 DoMenuCommand := recolorCmd;
  1136.                 END
  1137.             ELSE
  1138.                 DoMenuCommand := INHERITED DoMenuCommand(aCmdNumber);
  1139.             END;
  1140.  
  1141.     END;                                                {Case}
  1142.     END;
  1143.  
  1144. {--------------------------------------------------------------------------------------------------}
  1145. {$S ASelCommand}
  1146.  
  1147. FUNCTION TShapeView.DoMouseCommand(VAR theMouse: Point; VAR info: EventInfo;
  1148.                                    VAR hysteresis: Point): TCommand;
  1149.  
  1150.     VAR
  1151.         palette:            TPalette;
  1152.         protoShape:         TShape;
  1153.         shapeSketcher:        TShapeSketcher;
  1154.         shapeUnderMouse:    TShape;
  1155.         shapeSelector:        TShapeSelector;
  1156.         shapeDragger:        TShapeDragger;
  1157.         fi:                 FailInfo;
  1158.  
  1159.     PROCEDURE HdlInitCmdFailed(error: OSErr; message: LONGINT);
  1160.  
  1161.         BEGIN
  1162.         FreeIfObject(protoShape);
  1163.         protoShape := NIL;
  1164.         END;
  1165.  
  1166.     PROCEDURE CheckShape(aShape: TShape);
  1167.  
  1168.         BEGIN
  1169.         {$Push} {$h-}
  1170.         IF PtInRect(theMouse, aShape.fExtentRect) THEN
  1171.             shapeUnderMouse := aShape;
  1172.         {$Pop}
  1173.         END;
  1174.  
  1175.     BEGIN                                                { DoMouseCommand }
  1176.     DoMouseCommand := NIL;
  1177.  
  1178.     palette := fPalette;
  1179.     fClickPt := theMouse;
  1180.     IF palette.fCurrShape > 0 THEN                        {draw mode}
  1181.         BEGIN
  1182.         FailSpaceIsLow;                                 { Make sure we aren't low on memory }
  1183.  
  1184.         Deselect;
  1185.  
  1186.         {Clone appropriate shape}
  1187.  
  1188.         protoShape := TShape(gShapesArray[palette.fCurrShape].Clone);
  1189.         FailNil(protoShape);
  1190.  
  1191.         CatchFailures(fi, HdlInitCmdFailed);
  1192.         { Make sure cloning the shape left us with enough memory to continue.}
  1193.         FailSpaceIsLow;
  1194.  
  1195.         New(shapeSketcher);
  1196.         FailNil(shapeSketcher);
  1197.         shapeSketcher.IShapeSketcher(SELF, protoShape, info.theOptionKey);
  1198.         Success(fi);
  1199.         DoMouseCommand := shapeSketcher;
  1200.         END                                             {draw mode}
  1201.     ELSE
  1202.         BEGIN                                            {select mode}
  1203.         shapeUnderMouse := NIL;
  1204.         fShapeDocument.EachVirtualShapeDo(CheckShape);
  1205.  
  1206.         IF shapeUnderMouse = NIL THEN                    {area select}
  1207.             BEGIN
  1208.             IF NOT info.theShiftKey THEN
  1209.                 Deselect;
  1210.             New(shapeSelector);
  1211.             FailNil(shapeSelector);
  1212.             shapeSelector.IShapeSelector(cMouseCommand, SELF);
  1213.             DoMouseCommand := shapeSelector;
  1214.             END                                         {area select}
  1215.  
  1216.         ELSE
  1217.             BEGIN                                        {shape select/move/...}
  1218.  
  1219.             IF NOT (shapeUnderMouse.fIsSelected | info.theShiftKey) THEN
  1220.                 Deselect;
  1221.  
  1222.             IF info.theShiftKey THEN
  1223.                 BEGIN
  1224.                 shapeUnderMouse.fIsSelected := NOT shapeUnderMouse.fIsSelected;
  1225.                 IF shapeUnderMouse.fIsSelected THEN
  1226.                     shapeUnderMouse.Highlight(hlOff, hlOn)
  1227.                 ELSE
  1228.                     shapeUnderMouse.Highlight(hlOn, hlOff);
  1229.                 END
  1230.             ELSE IF NOT shapeUnderMouse.fIsSelected THEN
  1231.                 BEGIN
  1232.                 shapeUnderMouse.fIsSelected := TRUE;
  1233.                 DoHighlightSelection(hlOff, hlOn);
  1234.                 END;
  1235.  
  1236.             IF shapeUnderMouse.fIsSelected THEN
  1237.                 BEGIN
  1238.                 New(shapeDragger);
  1239.                 FailNil(shapeDragger);
  1240.                 shapeDragger.IShapeDragger(SELF);
  1241.                 DoMouseCommand := shapeDragger;
  1242.                 END;
  1243.             {ELSE, fall-through, we return NIL}
  1244.             END;                                        {shape select/move/...}
  1245.         END;                                            {Select mode}
  1246.     END;                                                { DoMouseCommand }
  1247.  
  1248. {--------------------------------------------------------------------------------------------------}
  1249. {$S ARes}
  1250.  
  1251. FUNCTION TShapeView.DoSetCursor(localPoint: Point; cursorRgn: RgnHandle): BOOLEAN; OVERRIDE;
  1252.  
  1253.     VAR
  1254.         cursorSet:            BOOLEAN;
  1255.         qdExtent:            Rect;
  1256.         shapeExtent:        Rect;
  1257.  
  1258.     PROCEDURE TestShape(shape: TShape);
  1259.  
  1260.         BEGIN
  1261.         {$Push} {$h-}
  1262.         IF PtInRect(localPoint, shape.fExtentRect) THEN
  1263.         {$Pop}
  1264.             BEGIN
  1265.             UseROMMap(TRUE);
  1266.             SetCursor(GetCursor(plusCursor)^^);
  1267.             shapeExtent := shape.fExtentRect;            {RectRgn may move memory}
  1268.             RectRgn(cursorRgn, shapeExtent);
  1269.             cursorSet := TRUE;
  1270.  
  1271.  {can't exit from the middle of an Each, because TList
  1272.  doesn't allow it; should really use FirstWhich;
  1273.  no harm in skipping the Exit, just takes longer}
  1274.             {* Exit(DoSetCursor); *}
  1275.             END;
  1276.         END;
  1277.  
  1278.     BEGIN                                                { DoSetCursor }
  1279.     IF fPalette.fCurrShape = 0 THEN                     {selection}
  1280.         BEGIN
  1281.         cursorSet := FALSE;
  1282.         fShapeDocument.EachVirtualShapeDo(TestShape);
  1283.         IF NOT cursorSet THEN
  1284.             IF qNeedsColorQD | gConfiguration.hasColorQD THEN
  1285.             {set cursor to color arrow}
  1286.                 SetCCursor(gRainbowArrow)
  1287.             ELSE
  1288.                 SetCursor(arrow);
  1289.         DoSetCursor := TRUE;
  1290.         END
  1291.     ELSE
  1292.         BEGIN
  1293.         DoSetCursor := TRUE;
  1294.         UseROMMap(TRUE);
  1295.         SetCursor(GetCursor(crossCursor)^^);
  1296.         GetQDExtent(qdExtent);
  1297.         RectRgn(cursorRgn, qdExtent);
  1298.         END;
  1299.     END;                                                { DoSetCursor }
  1300.  
  1301. {--------------------------------------------------------------------------------------------------}
  1302. {$S ARes}
  1303.  
  1304. PROCEDURE TShapeView.DoSetupMenus;
  1305.  
  1306.     VAR
  1307.         i:                    INTEGER;
  1308.         anySelection:        BOOLEAN;
  1309.         anyShapes:            BOOLEAN;
  1310.         haveMemory:         BOOLEAN;
  1311.         aMenuHandle:        MenuHandle;
  1312.         item:                INTEGER;
  1313.         itemName:            Str255;
  1314.  
  1315.     PROCEDURE TestShapes(theShape: TShape);
  1316.  
  1317.         BEGIN
  1318.         anySelection := anySelection | theShape.fIsSelected;
  1319.         anyShapes := anyShapes | (NOT fShapeDocument.fFiltering) | (NOT theShape.fWasSelected);
  1320.         END;
  1321.  
  1322.     BEGIN
  1323.  
  1324.     INHERITED DoSetupMenus;
  1325.  
  1326.     anySelection := FALSE;
  1327.     anyShapes := FALSE;
  1328.  
  1329.     haveMemory := NOT MemSpaceIsLow;
  1330.     { Find out if we are low on memory.  If we are then we'll disable all
  1331.       memory-intensive commands. }
  1332.  
  1333.     fShapeDocument.EachVirtualShapeDo(TestShapes);
  1334.     { This checks every virtual shape--could be made faster. }
  1335.  
  1336.     FOR i := cWhite TO cBlack DO
  1337.         Enable(i, anySelection);
  1338.  
  1339.     IF anySelection & (qNeedsColorQD | gConfiguration.hasColorQD) THEN
  1340.         BEGIN
  1341.         { Enable each of the Color menu items, if the Color menu is present }
  1342.         aMenuHandle := GetMHandle(mColor);
  1343.         IF aMenuHandle <> NIL THEN
  1344.             FOR item := 1 TO CountMItems(aMenuHandle) DO
  1345.                 BEGIN
  1346.  { There can be more than 31 menu entries with scrolling menus,
  1347.    but trying to enable an item with number > 31 is bad news.
  1348.    If the menu itself is enabled (which it will be in MacApp
  1349.    if any of the first 31 items is enabled), then the extras
  1350.    will always be enabled. }
  1351.                 {Don't enable line separators.}
  1352.                 GetItem(aMenuHandle, item, itemName);
  1353.                 IF (item <= 31) & (itemName <> '-') THEN
  1354.                     EnableItem(aMenuHandle, item);
  1355.                 END;
  1356.         END;
  1357.  
  1358.     {$IFC qDebug}
  1359.     Enable(cRecalcExtent, TRUE);
  1360.     {$ENDC}
  1361.     Enable(cCut, anySelection & haveMemory);
  1362.     Enable(cCopy, anySelection & haveMemory);
  1363.     IF haveMemory THEN
  1364.         CanPaste(kShapeClipType);
  1365.     Enable(cClear, anySelection);
  1366.  
  1367.     Enable(cSelectAll, anyShapes);
  1368.     EnableCheck(cBetterFeedback, TRUE, gBetterFeedback);
  1369.     END;                                                { DoSetupMenus }
  1370.  
  1371. {--------------------------------------------------------------------------------------------------}
  1372. {$S ARes}
  1373.  
  1374. PROCEDURE TShapeView.Draw(area: Rect);
  1375.  
  1376.     PROCEDURE DrawShape(shape: TShape);
  1377.  
  1378.         VAR
  1379.             r:                    Rect;
  1380.  
  1381.         BEGIN
  1382.         {$Push} {$h-}
  1383.         IF NOT (fDragging & shape.fIsSelected) & SectRect(shape.fExtentRect, area, r) THEN
  1384.             shape.Draw;
  1385.         {$Pop}
  1386.         END;
  1387.  
  1388.     BEGIN
  1389.     fShapeDocument.EachVirtualShapeDo(DrawShape);        {draw the shapes}
  1390.  
  1391.     INHERITED Draw(area);
  1392.     END;
  1393.  
  1394. {--------------------------------------------------------------------------------------------------}
  1395. {$S AFields}
  1396.  
  1397. PROCEDURE TShapeView.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  1398.                                                 fieldType: INTEGER)); OVERRIDE;
  1399.  
  1400.     BEGIN
  1401.     DoToField('TShapeView', NIL, bClass);
  1402.     DoToField('fDragging', @fDragging, bBoolean);
  1403.     DoToField('fPalette', @fPalette, bObject);
  1404.     DoToField('fClickPt', @fClickPt, bPoint);
  1405.     DoToField('fShapeDocument', @fShapeDocument, bObject);
  1406.     INHERITED Fields(DoToField);
  1407.     END;
  1408.  
  1409. {--------------------------------------------------------------------------------------------------}
  1410. {$S ARes}
  1411.  
  1412. PROCEDURE TShapeView.InvalShape(aShape: TShape);
  1413.  
  1414.     VAR
  1415.         r:                    Rect;
  1416.  
  1417.     BEGIN
  1418.     r := aShape.fExtentRect;
  1419.     InsetRect(r, - 2, - 2);
  1420.     InvalidRect(r);
  1421.     END;
  1422.  
  1423. {--------------------------------------------------------------------------------------------------}
  1424. {$S ARes}
  1425.  
  1426. PROCEDURE TShapeView.RestoreSelection;
  1427.  
  1428.     PROCEDURE DoRestore(aShape: TShape);
  1429.  
  1430.         VAR
  1431.             wantInval:            BOOLEAN;
  1432.  
  1433.         BEGIN
  1434.         wantInval := aShape.fIsSelected | aShape.fWasSelected;
  1435.         aShape.fIsSelected := aShape.fWasSelected;
  1436.         IF wantInval THEN
  1437.             InvalShape(aShape);
  1438.         END;
  1439.  
  1440.     BEGIN
  1441.     IF Focus THEN;
  1442.     Deselect;
  1443.     fShapeDocument.EachPotentialShapeDo(DoRestore);
  1444.     END;
  1445.  
  1446. {--------------------------------------------------------------------------------------------------}
  1447. {$S ARes}
  1448.  
  1449. PROCEDURE TShapeView.SaveSelection(andInval: BOOLEAN);
  1450.  
  1451.     PROCEDURE DoToShape(aShape: TShape);
  1452.  
  1453.         BEGIN
  1454.         WITH aShape DO
  1455.             fWasSelected := fIsSelected;
  1456.         IF andInval & aShape.fWasSelected THEN
  1457.             InvalShape(aShape);
  1458.         END;
  1459.  
  1460.     BEGIN
  1461.     IF Focus THEN;
  1462.     fShapeDocument.EachPotentialShapeDo(DoToShape);
  1463.     END;
  1464.  
  1465. {--------------------------------------------------------------------------------------------------}
  1466. {$S AClipboard}
  1467.  
  1468. PROCEDURE TShapeView.WriteToDeskScrap;
  1469.  
  1470.     VAR
  1471.         err:                LONGINT;
  1472.         clipShapes:         ShapesOnClipboard;
  1473.         count:                INTEGER;
  1474.         bBox:                Rect;
  1475.         i:                    INTEGER;
  1476.         outline:            Rect;
  1477.         aShapeDatum:        ShapeData;
  1478.         wantBytes:            INTEGER;
  1479.  
  1480.     PROCEDURE CopyToDeskScrap(aShape: TShape);
  1481.  
  1482.         BEGIN
  1483.         WITH aShapeDatum DO
  1484.             BEGIN
  1485.             theId := aShape.id;
  1486.             theShade := aShape.fShade;
  1487.             theColor := aShape.fColor;
  1488.             theRect := aShape.fExtentRect;
  1489.             END;
  1490.         clipShapes^^.theShapes[i] := aShapeDatum;
  1491.         i := i + 1;
  1492.         END;
  1493.  
  1494.     BEGIN
  1495.  { Write the PICT scrap first in case we will be unable to write both
  1496.    the PICT scrap and our own scrap type.}
  1497.     INHERITED WriteToDeskScrap;                         {Generate PICT-type scrap}
  1498.  
  1499.     fShapeDocument.SurveyShapes(FALSE, count, bBox);    {count shapes}
  1500.     wantBytes := SIZEOF(INTEGER) + SIZEOF(Rect) + (count * SIZEOF(ShapeData));
  1501.     {??? call CanAllocate, and if can't get enough, put up alert?}
  1502.     clipShapes := ShapesOnClipboard(NewPermHandle(wantBytes));
  1503.     FailNil(clipShapes);
  1504.  
  1505.     WITH clipShapes^^ DO
  1506.         BEGIN
  1507.         theBoundingBox := bBox;
  1508.         theNumberOfShapes := count;
  1509.         END;
  1510.     i := 0;
  1511.     fShapeDocument.EachShapeDo(CopyToDeskScrap);
  1512.     err := PutDeskScrapData(kShapeClipType, Handle(clipShapes));
  1513.     Handle(clipShapes) := DisposeIfHandle(clipShapes);
  1514.  
  1515.     FailOSErr(err);
  1516.     END;
  1517.  
  1518. {--------------------------------------------------------------------------------------------------}
  1519. {$S ARes}
  1520.  
  1521. PROCEDURE TShape.Initialize; OVERRIDE;
  1522.  
  1523.     BEGIN
  1524.     INHERITED Initialize;
  1525.  
  1526.     fID := 0;
  1527.     fExtentRect := gZeroRect;
  1528.     fShade := cWhite;
  1529.     fColor := gRGBWhite;
  1530.     fIsSelected := FALSE;
  1531.     fWasSelected := FALSE;
  1532.     END;
  1533.  
  1534. {--------------------------------------------------------------------------------------------------}
  1535. {$S ARes}
  1536.  
  1537. PROCEDURE TShape.IShape(itsExtent: Rect; itsID: INTEGER);
  1538.  
  1539.     BEGIN
  1540.     IObject;
  1541.     fExtentRect := itsExtent;
  1542.     fID := itsID;
  1543.     END;
  1544.  
  1545. {--------------------------------------------------------------------------------------------------}
  1546. {$S ARes}
  1547.  
  1548. PROCEDURE TShape.Draw;
  1549.  
  1550.     BEGIN
  1551.     END;
  1552.  
  1553. {--------------------------------------------------------------------------------------------------}
  1554. {$S ARes}
  1555.  
  1556. PROCEDURE TShape.DrawOutline;
  1557.  
  1558.     BEGIN
  1559.     {$IFC qDebug}
  1560.     ProgramBreak('TShape.DrawOutline called!');
  1561.     {$ENDC}
  1562.     END;
  1563.  
  1564. {--------------------------------------------------------------------------------------------------}
  1565. {$S ARes}
  1566.  
  1567. PROCEDURE TShape.EachHandleDo(PROCEDURE DoThis(Handle: Rect; handVHS: VHSelect;
  1568.                                                handTopOrLeft: BOOLEAN));
  1569.  
  1570.     VAR
  1571.         r:                    Rect;
  1572.         extent:             Rect;
  1573.  
  1574.     BEGIN
  1575.     SetRect(r, - 2, - 2, 2, 2);
  1576.     extent := fExtentRect;
  1577.  
  1578.     WITH extent DO
  1579.         BEGIN
  1580.         OffSetRect(r, left, (top + bottom) DIV 2);
  1581.         DoThis(r, h, TRUE);                             {left}
  1582.  
  1583.         OffSetRect(r, right - left, 0);
  1584.         DoThis(r, h, FALSE);                            {right}
  1585.  
  1586.         OffSetRect(r, - (right - left) DIV 2, top - ((top + bottom) DIV 2));
  1587.         DoThis(r, v, TRUE);                             {top}
  1588.  
  1589.         OffSetRect(r, 0, bottom - top);
  1590.         DoThis(r, v, FALSE);                            {bottom}
  1591.         END;
  1592.     END;
  1593.  
  1594. {--------------------------------------------------------------------------------------------------}
  1595. {$S AFields}
  1596.  
  1597. PROCEDURE TShape.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  1598.     fieldType: INTEGER)); OVERRIDE;
  1599.  
  1600.     BEGIN
  1601.     DoToField('TShape', NIL, bClass);
  1602.     DoToField('fID', @fID, bInteger);
  1603.     DoToField('fExtentRect', @fExtentRect, bRect);
  1604.     DoToField('fShade', @fShade, bInteger);
  1605.     DoToField('fOldShade', @fOldShade, bInteger);
  1606.     DoToField('fColor', @fColor, bRGBColor);
  1607.     DoToField('fOldColor', @fOldColor, bRGBColor);
  1608.     DoToField('fIsSelected', @fIsSelected, bBoolean);
  1609.     DoToField('fWasSelected', @fWasSelected, bBoolean);
  1610.     INHERITED Fields(DoToField);
  1611.     END;
  1612.  
  1613. {--------------------------------------------------------------------------------------------------}
  1614. {$S ARes}
  1615.  
  1616. PROCEDURE TShape.Highlight(fromHL, toHL: HLState);
  1617.  
  1618. {--------------------------------------------------------------------------------------------------}
  1619.  
  1620.     PROCEDURE PaintHandle(Handle: Rect; handVHS: VHSelect; handTopOrLeft: BOOLEAN);
  1621.  
  1622.         BEGIN
  1623.         PaintRect(Handle);
  1624.         END;
  1625.  
  1626.     BEGIN
  1627.     SetHLPenState(fromHL, toHL);
  1628.     EachHandleDo(PaintHandle);
  1629.     END;
  1630.  
  1631. {--------------------------------------------------------------------------------------------------}
  1632. {$S ARes}
  1633.  
  1634. FUNCTION TShape.id: INTEGER;
  1635.  
  1636.     BEGIN
  1637.     id := fID;
  1638.     END;
  1639.  
  1640. {--------------------------------------------------------------------------------------------------}
  1641. {$S AReadFile}
  1642.  
  1643. PROCEDURE TShape.ReadFrom(aRefNum: INTEGER);
  1644.  
  1645.     VAR
  1646.         data:                ShapeData;
  1647.         count:                LONGINT;
  1648.  
  1649.     BEGIN
  1650.     { DoRead has already read in the id; read the rest of the data }
  1651.     count := SIZEOF(ShapeData) - SIZEOF(INTEGER);
  1652.     FailOSErr(FSRead(aRefNum, count, @data.theRect));
  1653.  
  1654.     WITH data DO
  1655.         BEGIN
  1656.         fExtentRect := theRect;
  1657.         fShade := theShade;
  1658.         fColor := theColor;
  1659.         fIsSelected := theSelected;
  1660.         END;
  1661.     END;
  1662.  
  1663. {--------------------------------------------------------------------------------------------------}
  1664. {$S AWriteFile}
  1665.  
  1666. PROCEDURE TShape.WriteTo(aRefNum: INTEGER);
  1667.  
  1668.     VAR
  1669.         data:                ShapeData;
  1670.         count:                LONGINT;
  1671.  
  1672.     BEGIN
  1673.     WITH data DO
  1674.         BEGIN
  1675.         theId := id;
  1676.         theRect := fExtentRect;
  1677.         theShade := fShade;
  1678.         theColor := fColor;
  1679.         theSelected := fIsSelected;
  1680.         END;
  1681.     count := SIZEOF(ShapeData);
  1682.     FailOSErr(FSWrite(aRefNum, count, @data));
  1683.     END;
  1684.  
  1685. {--------------------------------------------------------------------------------------------------}
  1686. {$S ARes}
  1687.  
  1688. PROCEDURE TBox.IBox(itsExtent: Rect; itsID: INTEGER);
  1689.  
  1690.     BEGIN
  1691.     IShape(itsExtent, itsID);
  1692.     END;
  1693.  
  1694. {--------------------------------------------------------------------------------------------------}
  1695. {$S ARes}
  1696.  
  1697. PROCEDURE TBox.Draw;
  1698.  
  1699.     VAR
  1700.         itsExtent:            Rect;
  1701.         itsColor:            RGBColor;
  1702.  
  1703.     BEGIN
  1704.     PenNormal;
  1705.     itsExtent := fExtentRect;
  1706.  
  1707.     IF qNeedsColorQD | gConfiguration.hasColorQD THEN
  1708.         BEGIN
  1709.         {Get the color of the menu item representing the shape's color}
  1710.         itsColor := fColor;
  1711.         RGBForeColor(itsColor);
  1712.         FillRect(itsExtent, gPat[fShade]);
  1713.         ForeColor(blackColor);
  1714.         END
  1715.     ELSE
  1716.         FillRect(itsExtent, gPat[fShade]);
  1717.  
  1718.     DrawOutline;
  1719.     END;
  1720.  
  1721. {--------------------------------------------------------------------------------------------------}
  1722. {$S ARes}
  1723.  
  1724. PROCEDURE TBox.DrawOutline;
  1725.  
  1726.     VAR
  1727.         itsExtent:            Rect;
  1728.  
  1729.     BEGIN
  1730.     PenSize(1, 1);
  1731.     itsExtent := fExtentRect;                            {FrameRect may move memory.}
  1732.     FrameRect(itsExtent);
  1733.     END;
  1734.  
  1735. {--------------------------------------------------------------------------------------------------}
  1736. {$S ARes}
  1737.  
  1738. PROCEDURE THeavyBox.IHeavyBox(itsExtent: Rect; itsID: INTEGER);
  1739.  
  1740.     BEGIN
  1741.     IBox(itsExtent, itsID);
  1742.     END;
  1743.  
  1744. {--------------------------------------------------------------------------------------------------}
  1745. {$S ARes}
  1746.  
  1747. PROCEDURE THeavyBox.Draw;
  1748.  
  1749.     CONST
  1750.         s                    = '4K';
  1751.  
  1752.     VAR
  1753.         itsExtent:            Rect;
  1754.         wid:                INTEGER;
  1755.         r:                    Rect;
  1756.         x:                    INTEGER;
  1757.  
  1758.     BEGIN
  1759.     INHERITED Draw;
  1760.  
  1761.     itsExtent := fExtentRect;
  1762.  
  1763.     PenSize(2, 2);
  1764.     FrameRect(itsExtent);
  1765.  
  1766.     TextFont(geneva);
  1767.     TextFace([]);
  1768.     TextSize(9);
  1769.     TextMode(srcOR);
  1770.  
  1771.     wid := StringWidth(s);
  1772.  
  1773.     WITH itsExtent DO
  1774.         x := (left + right) DIV 2;
  1775.  
  1776.     SetRect(r, x - 7, itsExtent.bottom - 14, x + 7, itsExtent.bottom - 4);
  1777.     EraseRect(r);
  1778.     MADrawString(AtStr(s), r, teJustCenter);
  1779.     END;
  1780.  
  1781. {--------------------------------------------------------------------------------------------------}
  1782. {$S ARes}
  1783.  
  1784. PROCEDURE TCircle.ICircle(itsExtent: Rect; itsID: INTEGER);
  1785.  
  1786.     BEGIN
  1787.     IShape(itsExtent, itsID);
  1788.     END;
  1789.  
  1790. {--------------------------------------------------------------------------------------------------}
  1791. {$S ARes}
  1792.  
  1793. PROCEDURE TCircle.Draw;
  1794.  
  1795.     VAR
  1796.         itsExtent:            Rect;
  1797.         itsColor:            RGBColor;
  1798.  
  1799.     BEGIN
  1800.     PenNormal;
  1801.     itsExtent := fExtentRect;                            {FillOval may move memory}
  1802.  
  1803.     IF qNeedsColorQD | gConfiguration.hasColorQD THEN
  1804.         BEGIN
  1805.         {Get the color of the menu item representing the shape's color}
  1806.         itsColor := fColor;
  1807.         RGBForeColor(itsColor);
  1808.         FillOval(itsExtent, gPat[fShade]);
  1809.         ForeColor(blackColor);
  1810.         END
  1811.     ELSE
  1812.         FillOval(itsExtent, gPat[fShade]);
  1813.  
  1814.     DrawOutline;
  1815.     END;
  1816.  
  1817. {--------------------------------------------------------------------------------------------------}
  1818. {$S ARes}
  1819.  
  1820. PROCEDURE TCircle.DrawOutline;
  1821.  
  1822.     VAR
  1823.         itsExtent:            Rect;
  1824.  
  1825.     BEGIN
  1826.     PenSize(1, 1);
  1827.     itsExtent := fExtentRect;                            {FrameOval may move memory}
  1828.     FrameOval(itsExtent);
  1829.     END;
  1830.  
  1831. {--------------------------------------------------------------------------------------------------}
  1832. {$S ASelCommand}
  1833.  
  1834. PROCEDURE TShapeCommand.IShapeCommand(itsCmdNumber: CmdNumber; itsShapeView: TShapeView;
  1835.                                       betterFeedbackDesired: BOOLEAN);
  1836.  
  1837.     BEGIN
  1838.     IBetterFeedbackCmd(itsCmdNumber, itsShapeView.fShapeDocument, itsShapeView,
  1839.                        itsShapeView.fScroller, betterFeedbackDesired);
  1840.     fShapeView := itsShapeView;
  1841.     fShapeDocument := itsShapeView.fShapeDocument;
  1842.     END;
  1843.  
  1844. {--------------------------------------------------------------------------------------------------}
  1845. {$S AFields}
  1846.  
  1847. PROCEDURE TShapeCommand.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  1848.                                                    fieldType: INTEGER)); OVERRIDE;
  1849.  
  1850.     BEGIN
  1851.     DoToField('TShapeCommand', NIL, bClass);
  1852.     DoToField('fShapeView', @fShapeView, bObject);
  1853.     DoToField('fShapeDocument', @fShapeDocument, bObject);
  1854.     INHERITED Fields(DoToField);
  1855.     END;
  1856.  
  1857. {--------------------------------------------------------------------------------------------------}
  1858. {$S ASelCommand}
  1859.  
  1860. PROCEDURE TShapeSelector.IShapeSelector(itsCmdNumber: CmdNumber; itsShapeView: TShapeView);
  1861.  
  1862.     BEGIN
  1863.     IShapeCommand(itsCmdNumber, itsShapeView, gBetterFeedback);
  1864.     fCausesChange := FALSE;
  1865.     fCanUndo := FALSE;
  1866.     fBounds := gZeroRect;
  1867.     END;
  1868.  
  1869. {--------------------------------------------------------------------------------------------------}
  1870. {$S ADoCommand}
  1871.  
  1872. PROCEDURE TShapeSelector.DoIt; OVERRIDE;
  1873.  
  1874.     VAR
  1875.         shapeView:            TShapeView;
  1876.  
  1877.     PROCEDURE TestShape(shape: TShape);
  1878.  
  1879.         BEGIN
  1880.         {$Push} {$h-}
  1881.         IF RectsNest(fBounds, shape.fExtentRect) THEN
  1882.         {$Pop}
  1883.             BEGIN
  1884.             IF shape.fIsSelected THEN
  1885.                 shape.Highlight(hlOn, hlOff)
  1886.             ELSE
  1887.                 shape.Highlight(hlOff, hlOn);
  1888.             shape.fIsSelected := NOT shape.fIsSelected;
  1889.             END;
  1890.         END;
  1891.  
  1892.     BEGIN
  1893.     shapeView := fShapeView;
  1894.     shapeView.fShapeDocument.EachVirtualShapeDo(TestShape);
  1895.     END;
  1896.  
  1897. {--------------------------------------------------------------------------------------------------}
  1898. {$S AFields}
  1899.  
  1900. PROCEDURE TShapeSelector.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  1901.                                                     fieldType: INTEGER)); OVERRIDE;
  1902.  
  1903.     BEGIN
  1904.     DoToField('TShapeSelector', NIL, bClass);
  1905.     DoToField('fBounds', @fBounds, bRect);
  1906.     DoToField('fShiftKey', @fShiftKey, bBoolean);
  1907.     INHERITED Fields(DoToField);
  1908.     END;
  1909.  
  1910. {--------------------------------------------------------------------------------------------------}
  1911. {$S ADoCommand}
  1912.  
  1913. PROCEDURE TShapeSelector.TrackFeedback(anchorPoint, nextPoint: VPoint; turnItOn,
  1914.                                        mouseDidMove: BOOLEAN);
  1915.  
  1916.     VAR
  1917.         r:                    Rect;
  1918.  
  1919.     BEGIN
  1920.     INHERITED TrackFeedback(anchorPoint, nextPoint, turnItOn, mouseDidMove);
  1921.     IF (fView <> NIL) & mouseDidMove THEN
  1922.         BEGIN
  1923.  
  1924.         PenPat(Gray);
  1925.         Pt2Rect(fView.ViewToQDPt(anchorPoint), fView.ViewToQDPt(nextPoint), r);
  1926.         FrameRect(r);                                    { draw/erase }
  1927.  
  1928.         END;
  1929.     END;
  1930.  
  1931. {--------------------------------------------------------------------------------------------------}
  1932. {$S ADoCommand}
  1933.  
  1934. FUNCTION TShapeSelector.TrackMouse(aTrackPhase: TrackPhase; VAR anchorPoint, previousPoint,
  1935.                                    nextPoint: VPoint; mouseDidMove: BOOLEAN): TCommand;
  1936.  
  1937.     VAR
  1938.         r:                    Rect;
  1939.         qdAnchor, qdPrevious: Point;
  1940.  
  1941.     BEGIN
  1942.     TrackMouse := INHERITED TrackMouse(aTrackPhase, anchorPoint, previousPoint, nextPoint,
  1943.                                        mouseDidMove);
  1944.     qdAnchor := fShapeView.ViewToQDPt(anchorPoint);
  1945.     qdPrevious := fShapeView.ViewToQDPt(previousPoint);
  1946.     Pt2Rect(qdAnchor, qdPrevious, r);
  1947.     fBounds := r;
  1948.     END;
  1949.  
  1950. {--------------------------------------------------------------------------------------------------}
  1951. {$S ASelCommand}
  1952.  
  1953. PROCEDURE TShapeDragger.IShapeDragger(aShapeView: TShapeView);
  1954.  
  1955.     VAR
  1956.         bounds:             Rect;
  1957.         numberOfShapes:     INTEGER;
  1958.  
  1959.     BEGIN
  1960.     IShapeCommand(cMoveShape, aShapeView, gBetterFeedback);
  1961.  
  1962.     aShapeView.fShapeDocument.SurveyShapes(TRUE, numberOfShapes, bounds);
  1963.     aShapeView.fDragging := FALSE;
  1964.     fBounds := bounds;
  1965.  
  1966.     fConstrainsMouse := gConstrainDrags;
  1967.     END;
  1968.  
  1969. {--------------------------------------------------------------------------------------------------}
  1970. {$S ADoCommand}
  1971.  
  1972. PROCEDURE TShapeDragger.DoIt;
  1973.  
  1974.     BEGIN
  1975.     MoveBy(fDeltaH, fDeltaV);
  1976.     END;
  1977.  
  1978. {--------------------------------------------------------------------------------------------------}
  1979. {$S AFields}
  1980.  
  1981. PROCEDURE TShapeDragger.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  1982.                                                    fieldType: INTEGER)); OVERRIDE;
  1983.  
  1984.     BEGIN
  1985.     DoToField('TShapeDragger', NIL, bClass);
  1986.     DoToField('fBounds', @fBounds, bRect);
  1987.     DoToField('fDeltaH', @fDeltaH, bInteger);
  1988.     DoToField('fDeltaV', @fDeltaV, bInteger);
  1989.     INHERITED Fields(DoToField);
  1990.     END;
  1991.  
  1992. {--------------------------------------------------------------------------------------------------}
  1993. {$S ADoCommand}
  1994.  
  1995. PROCEDURE TShapeDragger.MoveBy(deltaH, deltaV: INTEGER);
  1996.  
  1997.     PROCEDURE MoveShape(shape: TShape);
  1998.  
  1999.         BEGIN
  2000.         IF shape.fIsSelected THEN
  2001.             BEGIN
  2002.             {$Push} {$h-}
  2003.             OffSetRect(shape.fExtentRect, deltaH, deltaV);
  2004.             {$Pop}
  2005.             fShapeView.InvalShape(shape);
  2006.             END;
  2007.         shape.fWasSelected := shape.fIsSelected;
  2008.         END;
  2009.  
  2010.     BEGIN
  2011.     IF fShapeView.Focus THEN;
  2012.     fShapeDocument.EachShapeDo(MoveShape);
  2013.     END;
  2014.  
  2015. {--------------------------------------------------------------------------------------------------}
  2016. {$S ADoCommand}
  2017.  
  2018. PROCEDURE TShapeDragger.RedoIt;
  2019.  
  2020.     BEGIN
  2021.     fShapeView.RestoreSelection;
  2022.  
  2023.     MoveBy(fDeltaH, fDeltaV);
  2024.     END;
  2025.  
  2026. {--------------------------------------------------------------------------------------------------}
  2027. {$S ADoCommand}
  2028.  
  2029. PROCEDURE TShapeDragger.TrackConstrain(anchorPoint, previousPoint: VPoint;
  2030.     VAR nextPoint: VPoint); OVERRIDE;
  2031.  
  2032.     VAR
  2033.         vhs:                VHSelect;
  2034.         temp:                INTEGER;
  2035.  
  2036.     BEGIN
  2037.     FOR vhs := v TO h DO
  2038.         BEGIN
  2039.         temp := anchorPoint.vh[vhs] - fBounds.topLeft.vh[vhs];
  2040.         nextPoint.vh[vhs] := Max(temp, nextPoint.vh[vhs]);
  2041.  
  2042.         temp := fShapeView.fSize.vh[vhs] - (fBounds.botRight.vh[vhs] - anchorPoint.vh[vhs]);
  2043.         nextPoint.vh[vhs] := Min(temp, nextPoint.vh[vhs]);
  2044.         END;
  2045.     END;
  2046.  
  2047. {--------------------------------------------------------------------------------------------------}
  2048. {$S ADoCommand}
  2049.  
  2050. PROCEDURE TShapeDragger.TrackFeedback(anchorPoint, nextPoint: VPoint; turnItOn,
  2051.                                       mouseDidMove: BOOLEAN);
  2052.  
  2053.     VAR
  2054.         aRect:                Rect;
  2055.         delta:                Point;
  2056.  
  2057.     BEGIN
  2058.     INHERITED TrackFeedback(anchorPoint, nextPoint, turnItOn, mouseDidMove);
  2059.     IF mouseDidMove & fShapeView.fDragging THEN
  2060.         BEGIN
  2061.         delta.h := nextPoint.h - anchorPoint.h;
  2062.         delta.v := nextPoint.v - anchorPoint.v;
  2063.  
  2064.         aRect := fBounds;
  2065.         OffSetRect(aRect, delta.h, delta.v);
  2066.  
  2067.         FrameRect(aRect);                                { draw/erase it }
  2068.         END;
  2069.     END;
  2070.  
  2071. {--------------------------------------------------------------------------------------------------}
  2072. {$S ADoCommand}
  2073.  
  2074. FUNCTION TShapeDragger.TrackMouse(aTrackPhase: TrackPhase; VAR anchorPoint, previousPoint,
  2075.                                   nextPoint: VPoint; mouseDidMove: BOOLEAN): TCommand;
  2076.  
  2077.     PROCEDURE EraseShape(shape: TShape);
  2078.  
  2079.         VAR
  2080.             r:                    Rect;
  2081.  
  2082.         BEGIN
  2083.         IF shape.fIsSelected THEN
  2084.             fShapeView.InvalShape(shape);
  2085.         END;
  2086.  
  2087.     BEGIN
  2088.     TrackMouse := INHERITED TrackMouse(aTrackPhase, anchorPoint, previousPoint, nextPoint,
  2089.                                        mouseDidMove);
  2090.  
  2091.     IF aTrackPhase = trackRelease THEN                    {set up for moving the shape(s)}
  2092.         BEGIN
  2093.         IF fShapeView.fDragging THEN                    {actually did move}
  2094.             BEGIN
  2095.             fDeltaH := previousPoint.h - anchorPoint.h;
  2096.             fDeltaV := previousPoint.v - anchorPoint.v;
  2097.  
  2098.             fShapeView.fDragging := FALSE;
  2099.             END
  2100.         ELSE
  2101.             TrackMouse := NIL;
  2102.         END
  2103.  
  2104.     ELSE IF aTrackPhase = trackMove THEN
  2105.         IF mouseDidMove THEN
  2106.             IF NOT fShapeView.fDragging THEN            {this is first move}
  2107.                 BEGIN
  2108.                 fShapeView.DoHighlightSelection(hlOn, hlOff);
  2109.                 fShapeDocument.EachVirtualShapeDo(EraseShape);
  2110.                 fShapeView.fDragging := TRUE;
  2111.                 fShapeView.GetWindow.Update;
  2112.                 IF fShapeView.Focus THEN;                {UpdateEvent changes the focus - restore it}
  2113.                 END;
  2114.     END;
  2115.  
  2116. {--------------------------------------------------------------------------------------------------}
  2117. {$S ADoCommand}
  2118.  
  2119. PROCEDURE TShapeDragger.UndoIt;
  2120.  
  2121.     BEGIN
  2122.     fShapeView.RestoreSelection;
  2123.  
  2124.     MoveBy( - fDeltaH, - fDeltaV);
  2125.     END;
  2126.  
  2127. {--------------------------------------------------------------------------------------------------}
  2128. {$S ASelCommand}
  2129.  
  2130. PROCEDURE TReshadeCmd.IReshadeCmd(itsCmdNumber: INTEGER; itsShapeView: TShapeView);
  2131.  
  2132.     BEGIN
  2133.     IShapeCommand(cChangeShade, itsShapeView, NOT kBetterFeedbackDesired);
  2134.     fShade := itsCmdNumber;
  2135.     END;
  2136.  
  2137. {--------------------------------------------------------------------------------------------------}
  2138. {$S ADoCommand}
  2139.  
  2140. PROCEDURE TReshadeCmd.DoIt;
  2141.  
  2142.     PROCEDURE ReshadeShape(shape: TShape);
  2143.  
  2144.         VAR
  2145.             menu, item:         INTEGER;
  2146.  
  2147.         BEGIN
  2148.         IF shape.fIsSelected THEN
  2149.             BEGIN
  2150.             shape.fOldShade := shape.fShade;
  2151.             shape.fShade := fShade;
  2152.             fShapeView.InvalShape(shape);
  2153.             END;
  2154.         shape.fWasSelected := shape.fIsSelected;
  2155.         END;
  2156.  
  2157.     BEGIN
  2158.     IF fShapeView.Focus THEN;
  2159.     fShapeDocument.EachShapeDo(ReshadeShape);
  2160.     END;
  2161.  
  2162. {--------------------------------------------------------------------------------------------------}
  2163. {$S ADoCommand}
  2164.  
  2165. PROCEDURE TReshadeCmd.RedoIt;
  2166.  
  2167.     PROCEDURE ReshadeShape(shape: TShape);
  2168.  
  2169.         VAR
  2170.             r:                    Rect;
  2171.             menu, item:         INTEGER;
  2172.  
  2173.         BEGIN
  2174.         IF shape.fWasSelected THEN
  2175.             BEGIN
  2176.             shape.fOldShade := shape.fShade;
  2177.             shape.fShade := fShade;
  2178.             fShapeView.InvalShape(shape);
  2179.             END;
  2180.         shape.fIsSelected := shape.fWasSelected;
  2181.         END;
  2182.  
  2183.     BEGIN
  2184.     IF fShapeView.Focus THEN;
  2185.     fShapeView.Deselect;
  2186.     fShapeDocument.EachShapeDo(ReshadeShape);
  2187.     END;
  2188.  
  2189. {--------------------------------------------------------------------------------------------------}
  2190. {$S ADoCommand}
  2191.  
  2192. PROCEDURE TReshadeCmd.UndoIt;
  2193.  
  2194.     PROCEDURE ReshadeShape(shape: TShape);
  2195.  
  2196.         VAR
  2197.             r:                    Rect;
  2198.  
  2199.         BEGIN
  2200.         IF shape.fWasSelected THEN
  2201.             BEGIN
  2202.             shape.fShade := shape.fOldShade;
  2203.             fShapeView.InvalShape(shape);
  2204.             END;
  2205.         shape.fIsSelected := shape.fWasSelected;
  2206.         END;
  2207.  
  2208.     BEGIN
  2209.     IF fShapeView.Focus THEN;
  2210.     fShapeView.Deselect;
  2211.     fShapeDocument.EachShapeDo(ReshadeShape);
  2212.     END;
  2213.  
  2214. {--------------------------------------------------------------------------------------------------}
  2215. {$S ASelCommand}
  2216.  
  2217. PROCEDURE TRecolorCmd.IRecolorCmd(itsColor: RGBColor; itsShapeView: TShapeView);
  2218.  
  2219.     BEGIN
  2220.     IShapeCommand(cChangeColor, itsShapeView, NOT kBetterFeedbackDesired);
  2221.     fColor := itsColor;
  2222.     END;
  2223.  
  2224. {--------------------------------------------------------------------------------------------------}
  2225. {$S ADoCommand}
  2226.  
  2227. PROCEDURE TRecolorCmd.DoIt;
  2228.  
  2229.     PROCEDURE RecolorShape(shape: TShape);
  2230.  
  2231.         VAR
  2232.             menu, item:         INTEGER;
  2233.  
  2234.         BEGIN
  2235.         IF shape.fIsSelected THEN
  2236.             BEGIN
  2237.             shape.fOldColor := shape.fColor;
  2238.             shape.fColor := fColor;
  2239.             fShapeView.InvalShape(shape);
  2240.             END;
  2241.         shape.fWasSelected := shape.fIsSelected;
  2242.         END;
  2243.  
  2244.     BEGIN
  2245.     IF fShapeView.Focus THEN;
  2246.     fShapeDocument.EachShapeDo(RecolorShape);
  2247.     END;
  2248.  
  2249. {--------------------------------------------------------------------------------------------------}
  2250. {$S ADoCommand}
  2251.  
  2252. PROCEDURE TRecolorCmd.RedoIt;
  2253.  
  2254.     PROCEDURE RecolorShape(shape: TShape);
  2255.  
  2256.         VAR
  2257.             r:                    Rect;
  2258.             menu, item:         INTEGER;
  2259.  
  2260.         BEGIN
  2261.         IF shape.fWasSelected THEN
  2262.             BEGIN
  2263.             shape.fOldColor := shape.fColor;
  2264.             shape.fColor := fColor;
  2265.             fShapeView.InvalShape(shape);
  2266.             END;
  2267.         shape.fIsSelected := shape.fWasSelected;
  2268.         END;
  2269.  
  2270.     BEGIN
  2271.     IF fShapeView.Focus THEN;
  2272.     fShapeView.Deselect;
  2273.     fShapeDocument.EachShapeDo(RecolorShape);
  2274.     END;
  2275.  
  2276. {--------------------------------------------------------------------------------------------------}
  2277. {$S ADoCommand}
  2278.  
  2279. PROCEDURE TRecolorCmd.UndoIt;
  2280.  
  2281.     PROCEDURE RecolorShape(shape: TShape);
  2282.  
  2283.         VAR
  2284.             r:                    Rect;
  2285.  
  2286.         BEGIN
  2287.         IF shape.fWasSelected THEN
  2288.             BEGIN
  2289.             shape.fColor := shape.fOldColor;
  2290.             fShapeView.InvalShape(shape);
  2291.             END;
  2292.         shape.fIsSelected := shape.fWasSelected;
  2293.         END;
  2294.  
  2295.     BEGIN
  2296.     IF fShapeView.Focus THEN;
  2297.     fShapeView.Deselect;
  2298.     fShapeDocument.EachShapeDo(RecolorShape);
  2299.     END;
  2300.  
  2301. {--------------------------------------------------------------------------------------------------}
  2302. {$S ADoCommand}
  2303.  
  2304. PROCEDURE TShapeReplaceCommand.Commit; OVERRIDE;
  2305.  
  2306.     PROCEDURE HandleIt(aShape: TShape);
  2307.  
  2308.         BEGIN
  2309.         IF aShape.fWasSelected THEN
  2310.             fShapeDocument.DeleteShape(aShape);
  2311.         END;
  2312.  
  2313.     BEGIN
  2314.     IF fShapeDocument.fFiltering THEN
  2315.         fShapeDocument.EachShapeDo(HandleIt);
  2316.     fShapeDocument.fFiltering := FALSE;
  2317.     fShapeDocument.fReplaceCommand := NIL;
  2318.     END;
  2319.  
  2320. {--------------------------------------------------------------------------------------------------}
  2321. {$S ARes}
  2322.  
  2323. PROCEDURE TShapeReplaceCommand.EachNewShapeDo(PROCEDURE
  2324.                                               DoThis(shape: TShape));
  2325.  
  2326.     BEGIN
  2327.     END;
  2328.  
  2329. {--------------------------------------------------------------------------------------------------}
  2330. {$ARes}
  2331.  
  2332. FUNCTION TShapeReplaceCommand.FirstShapeThat(FUNCTION
  2333.                                              TestShape(aShape: TShape): BOOLEAN): TShape;
  2334.  
  2335.     BEGIN
  2336.     FirstShapeThat := NIL;
  2337.     END;
  2338.  
  2339. {--------------------------------------------------------------------------------------------------}
  2340. {$S ADoCommand}
  2341.  
  2342. PROCEDURE TShapeReplaceCommand.RedoIt; OVERRIDE;
  2343.  
  2344.     BEGIN
  2345.     IF fShapeDocument.fFiltering THEN
  2346.         fShapeView.RestoreSelection;
  2347.     fShapeDocument.fReplaceCommand := SELF;
  2348.     END;
  2349.  
  2350. {--------------------------------------------------------------------------------------------------}
  2351. {$S ADoCommand}
  2352.  
  2353. PROCEDURE TShapeReplaceCommand.UndoIt; OVERRIDE;
  2354.  
  2355.     BEGIN
  2356.     fShapeView.RestoreSelection;
  2357.     fShapeDocument.fReplaceCommand := NIL;
  2358.     fShapeDocument.fFiltering := FALSE;
  2359.     END;
  2360.  
  2361. {--------------------------------------------------------------------------------------------------}
  2362. {$S ASelCommand}
  2363.  
  2364. PROCEDURE TShapeSketcher.IShapeSketcher(aShapeView: TShapeView; protoShape: TShape;
  2365.                                         constrain: BOOLEAN);
  2366.  
  2367.     BEGIN
  2368.     IShapeCommand(cNewShape, aShapeView, gBetterFeedback);
  2369.     fShape := protoShape;
  2370.     fConstrainsMouse := constrain;
  2371.     END;
  2372.  
  2373. {--------------------------------------------------------------------------------------------------}
  2374. {$S ADoCommand}
  2375.  
  2376. PROCEDURE TShapeSketcher.Free;
  2377.  
  2378.     BEGIN
  2379.     FreeIfObject(fShape);
  2380.     fShape := NIL;
  2381.  
  2382.     INHERITED Free;
  2383.     END;
  2384.  
  2385. {--------------------------------------------------------------------------------------------------}
  2386. {$S ADoCommand}
  2387.  
  2388. PROCEDURE TShapeSketcher.DoIt;
  2389.  
  2390.     VAR
  2391.         pMCEntry:            MCEntryPtr;
  2392.  
  2393.     BEGIN
  2394.     fShape.fIsSelected := TRUE;
  2395.     fShape.fShade := cShadeBase + ABS(Random MOD kNoOfShades);
  2396.     IF qNeedsColorQD | gConfiguration.hasColorQD THEN
  2397.     { Pick a random color from the Color menu }
  2398.         BEGIN
  2399.         pMCEntry := GetMCEntry(mColor, ABS(Random MOD CountMItems(GetMHandle(mColor))) + 1);
  2400.         fShape.fColor := pMCEntry^.mctRGB2;
  2401.         END
  2402.     ELSE
  2403.         fShape.fColor := gRGBBlack;                     { Set color to black }
  2404.  
  2405.     fShapeDocument.fReplaceCommand := SELF;
  2406.     fShapeView.InvalShape(fShape);
  2407.     END;
  2408.  
  2409. {--------------------------------------------------------------------------------------------------}
  2410. {$S ADoCommand}
  2411.  
  2412. PROCEDURE TShapeSketcher.UndoIt;
  2413.  
  2414.     BEGIN
  2415.     INHERITED UndoIt;
  2416.     fShapeView.InvalShape(fShape);
  2417.     END;
  2418.  
  2419. {--------------------------------------------------------------------------------------------------}
  2420. {$S ADoCommand}
  2421.  
  2422. PROCEDURE TShapeSketcher.RedoIt;
  2423.  
  2424.     BEGIN
  2425.     IF fShapeView.Focus THEN;
  2426.     fShapeView.Deselect;
  2427.     INHERITED RedoIt;
  2428.     fShape.fIsSelected := TRUE;
  2429.     fShapeView.InvalShape(fShape);
  2430.     END;
  2431.  
  2432. {--------------------------------------------------------------------------------------------------}
  2433. {$S ADoCommand}
  2434.  
  2435. PROCEDURE TShapeSketcher.Commit;
  2436.  
  2437.     BEGIN
  2438.     fShapeDocument.AddShape(fShape);                    { Add the shape to the list }
  2439.     { Set this field to NIL to prevent Free from freeing it}
  2440.     fShape := NIL;
  2441.     INHERITED Commit;
  2442.     END;
  2443.  
  2444. {--------------------------------------------------------------------------------------------------}
  2445. {$S ARes}
  2446.  
  2447. PROCEDURE TShapeSketcher.EachNewShapeDo(PROCEDURE DoThis(shape: TShape)); OVERRIDE;
  2448.  
  2449.     BEGIN
  2450.     DoThis(fShape);                                     { there's only one shape }
  2451.     END;
  2452.  
  2453. {--------------------------------------------------------------------------------------------------}
  2454. {$ARes}
  2455.  
  2456. FUNCTION TShapeSketcher.FirstShapeThat(FUNCTION TestShape(aShape: TShape): BOOLEAN): TShape;
  2457.     OVERRIDE;
  2458.  
  2459.     BEGIN
  2460.     IF TestShape(fShape) THEN                            { there's only one shape to test }
  2461.         FirstShapeThat := fShape
  2462.     ELSE
  2463.         FirstShapeThat := NIL;
  2464.     END;
  2465.  
  2466. {--------------------------------------------------------------------------------------------------}
  2467. {$S AFields}
  2468.  
  2469. PROCEDURE TShapeSketcher.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  2470.                                                     fieldType: INTEGER)); OVERRIDE;
  2471.  
  2472.     BEGIN
  2473.     DoToField('TShapeSketcher', NIL, bClass);
  2474.     DoToField('fShape', @fShape, bRect);
  2475.     INHERITED Fields(DoToField);
  2476.     END;
  2477.  
  2478. {--------------------------------------------------------------------------------------------------}
  2479. {$S ADoCommand}
  2480.  
  2481. PROCEDURE TShapeSketcher.TrackConstrain(anchorPoint, previousPoint: VPoint;
  2482.     VAR nextPoint: VPoint); OVERRIDE;
  2483.  
  2484.     VAR
  2485.         dh:                 INTEGER;
  2486.         dv:                 INTEGER;
  2487.         absDh:                INTEGER;
  2488.         absDv:                INTEGER;
  2489.         delta:                INTEGER;
  2490.  
  2491.     BEGIN
  2492.     dh := nextPoint.h - anchorPoint.h;
  2493.     dv := nextPoint.v - anchorPoint.v;
  2494.     absDh := ABS(dh);
  2495.     absDv := ABS(dv);
  2496.     delta := Min(absDh, absDv);
  2497.     IF dh < 0 THEN
  2498.         dh := - delta
  2499.     ELSE
  2500.         dh := delta;
  2501.     IF dv < 0 THEN
  2502.         dv := - delta
  2503.     ELSE
  2504.         dv := delta;
  2505.     nextPoint.h := anchorPoint.h + dh;
  2506.     nextPoint.v := anchorPoint.v + dv;
  2507.     END;
  2508.  
  2509. {--------------------------------------------------------------------------------------------------}
  2510. {$S ADoCommand}
  2511.  
  2512. PROCEDURE TShapeSketcher.TrackFeedback(anchorPoint, nextPoint: VPoint; turnItOn,
  2513.                                        mouseDidMove: BOOLEAN);
  2514.  
  2515.     BEGIN
  2516.     INHERITED TrackFeedback(anchorPoint, nextPoint, turnItOn, mouseDidMove);
  2517.     IF mouseDidMove & (fShape <> NIL) THEN
  2518.         fShape.DrawOutline;                             { draw/erase }
  2519.     END;
  2520.  
  2521. {--------------------------------------------------------------------------------------------------}
  2522. {$S ADoCommand}
  2523.  
  2524. FUNCTION TShapeSketcher.TrackMouse(aTrackPhase: TrackPhase; VAR anchorPoint, previousPoint,
  2525.                                    nextPoint: VPoint; mouseDidMove: BOOLEAN): TCommand;
  2526.  
  2527.     VAR
  2528.         aVRect:             VRect;
  2529.         r:                    Rect;
  2530.         size:                Point;
  2531.         bigEnough:            BOOLEAN;
  2532.  
  2533.     BEGIN
  2534.     TrackMouse := INHERITED TrackMouse(aTrackPhase, anchorPoint, previousPoint, nextPoint,
  2535.                                        mouseDidMove);
  2536.  
  2537.     IF aTrackPhase = trackRelease THEN                    {create the new shape}
  2538.         BEGIN
  2539.         bigEnough := FALSE;
  2540.  
  2541.         size.h := nextPoint.h - anchorPoint.h;
  2542.         size.v := nextPoint.v - anchorPoint.v;
  2543.         IF ABS(size.h) >= kMinWidth THEN
  2544.             IF ABS(size.v) >= kMinHeight THEN
  2545.                 bigEnough := TRUE;
  2546.  
  2547.         IF NOT bigEnough THEN
  2548.             BEGIN
  2549.             FreeIfObject(fShape);
  2550.             fShape := NIL;
  2551.  
  2552.             TrackMouse := NIL;
  2553.             END;
  2554.         END
  2555.     ELSE
  2556.         BEGIN
  2557.         Pt2VRect(anchorPoint, nextPoint, aVRect);
  2558.         VRectToRect(aVRect, r);                         { okay because we know the view's size is in
  2559.                                                          QD dimensions }
  2560.         fShape.fExtentRect := r;
  2561.         END;
  2562.     END;
  2563.  
  2564. {--------------------------------------------------------------------------------------------------}
  2565. {$S ASelCommand}
  2566.  
  2567. PROCEDURE TShapeCutCopyCommand.IShapeCutCopyCommand(itsCmdNumber: CmdNumber;
  2568.                                                     itsShapeView: TShapeView);
  2569.  
  2570.     BEGIN
  2571.     IShapeCommand(itsCmdNumber, itsShapeView, NOT kBetterFeedbackDesired);
  2572.     fChangesClipboard := TRUE;
  2573.     fCausesChange := itsCmdNumber = cCut;
  2574.     END;
  2575.  
  2576. {--------------------------------------------------------------------------------------------------}
  2577. {$S ADoCommand}
  2578.  
  2579. PROCEDURE TShapeCutCopyCommand.DoIt;
  2580.  
  2581.     VAR
  2582.         succeeded:            BOOLEAN;
  2583.         clipShapeView:        TShapeView;
  2584.         clipDoc:            TShapeDocument;
  2585.         selTopLeft:         Point;
  2586.         count:                INTEGER;
  2587.         outline:            Rect;
  2588.         fi:                 FailInfo;
  2589.  
  2590. {--------------------------------------------------------------------------------------------------}
  2591.  
  2592.     PROCEDURE CopyToClip(shape: TShape);
  2593.  
  2594.         VAR
  2595.             aNewShape:            TShape;
  2596.             r:                    Rect;
  2597.  
  2598.         BEGIN
  2599.         IF shape.fIsSelected THEN
  2600.             BEGIN
  2601.             aNewShape := TShape(shape.Clone);
  2602.             FailNil(aNewShape);
  2603.             WITH aNewShape DO
  2604.                 BEGIN
  2605.                 fIsSelected := FALSE;
  2606.                 fWasSelected := FALSE;
  2607.                 END;
  2608.             r := aNewShape.fExtentRect;
  2609.             OffSetRect(r, gClipMargin.h - outline.left, gClipMargin.v - outline.top);
  2610.             aNewShape.fExtentRect := r;
  2611.  
  2612.             clipDoc.AddShape(aNewShape);
  2613.             END;
  2614.         END;
  2615.  
  2616.     PROCEDURE HdlFailure(error: OSErr; message: LONGINT);
  2617.  
  2618.         BEGIN
  2619.         FreeIfObject(clipDoc);
  2620.         clipDoc := NIL;
  2621.         END;
  2622.  
  2623.     BEGIN                                                { TShapeCutCopyCommand.DoIt }
  2624.     fShapeView.SaveSelection(fCmdNumber = cCut);        {don't inval if it's just COPY}
  2625.     New(clipDoc);
  2626.     FailNil(clipDoc);
  2627.     clipDoc.IShapeDocument(kDocType);
  2628.  
  2629.     CatchFailures(fi, HdlFailure);
  2630.     fShapeDocument.SurveyShapes(TRUE, count, outline);
  2631.  
  2632.     New(clipShapeView);
  2633.     FailNil(clipShapeView);
  2634.     clipShapeView.IShapeView(clipDoc, NIL, TRUE);
  2635.  
  2636.     { Set fShapeView since the doc will NOT be told to DoMakeViews }
  2637.     clipDoc.fShapeView := clipShapeView;
  2638.  
  2639.     IF fCmdNumber = cCut THEN
  2640.         IF fShapeView.Focus THEN;
  2641.  
  2642.     fShapeDocument.EachShapeDo(CopyToClip);
  2643.     clipShapeView.AdjustSize;
  2644.  
  2645.     { Make sure the Cut left us with enough memory to continue. }
  2646.     FailSpaceIsLow;
  2647.     Success(fi);
  2648.  
  2649.     gApplication.ClaimClipboard(clipShapeView);
  2650.  
  2651.     fShapeDocument.fFiltering := (fCmdNumber <> cCopy);
  2652.  
  2653.     fShapeDocument.fReplaceCommand := SELF;
  2654.     END;
  2655.  
  2656. {--------------------------------------------------------------------------------------------------}
  2657. {$S ADoCommand}
  2658.  
  2659. PROCEDURE TShapeCutCopyCommand.RedoIt;
  2660.  
  2661.     BEGIN
  2662.     fShapeDocument.fFiltering := (fCmdNumber <> cCopy);
  2663.     INHERITED RedoIt;
  2664.     END;
  2665.  
  2666. {--------------------------------------------------------------------------------------------------}
  2667. {$S ASelCommand}
  2668.  
  2669. PROCEDURE TShapePasteCommand.IShapePasteCommand(itsShapeView: TShapeView);
  2670.  
  2671.     VAR
  2672.         fi:                 FailInfo;
  2673.  
  2674.     PROCEDURE HdlFailure(error: OSErr; message: LONGINT);
  2675.  
  2676.         BEGIN
  2677.         FreeIfObject(SELF);
  2678.         END;
  2679.  
  2680.     BEGIN
  2681.     fPasteList := NIL;                                    { So free works when we can't allocated it }
  2682.     IShapeCommand(cPaste, itsShapeView, NOT kBetterFeedbackDesired);
  2683.     CatchFailures(fi, HdlFailure);
  2684.     fPasteList := NewList;
  2685.     Success(fi);
  2686.     {$IFC qDebug}
  2687.     fPasteList.SetEltType('TShape');
  2688.     {$ENDC}
  2689.     END;
  2690.  
  2691. {--------------------------------------------------------------------------------------------------}
  2692. {$S ADoCommand}
  2693.  
  2694. PROCEDURE TShapePasteCommand.Commit; OVERRIDE;
  2695.  
  2696.     VAR
  2697.         theShape:            TShape;
  2698.  
  2699.     BEGIN
  2700.     IF gPasteReplacesSelection THEN
  2701.         INHERITED Commit;                                {Deletes old selectees from the document}
  2702.  
  2703.  { The following loop transfers shapes from fPasteList to the document's
  2704.    shape list, in such a way that fPasteList is shrunk while the
  2705.    document's shape list is grown.  This helps prevent running out
  2706.    of memory when committing a Paste command. }
  2707.     theShape := TShape(fPasteList.First);
  2708.     WHILE theShape <> NIL DO
  2709.         BEGIN
  2710.         fPasteList.Delete(theShape);
  2711.         fShapeDocument.AddShape(theShape);
  2712.         theShape := TShape(fPasteList.First);
  2713.         END;
  2714.  
  2715.     fShapeDocument.fReplaceCommand := NIL;
  2716.     END;
  2717.  
  2718. {--------------------------------------------------------------------------------------------------}
  2719. {$S ADoCommand}
  2720.  
  2721. PROCEDURE TShapePasteCommand.DoIt; OVERRIDE;
  2722.  
  2723.     VAR
  2724.         whereToPaste:        Point;
  2725.         noOfShapes:         INTEGER;
  2726.         translation:        VPoint;
  2727.         t:                    INTEGER;
  2728.         vhs:                VHSelect;
  2729.         extent:             Rect;
  2730.         scrollerExtent:     VRect;
  2731.  
  2732.     PROCEDURE PasteShape(clipShape: TShape);
  2733.  
  2734.         VAR
  2735.             aShape:             TShape;
  2736.  
  2737.         BEGIN
  2738.         aShape := TShape(clipShape.Clone);
  2739.         FailNil(aShape);
  2740.         WITH aShape DO
  2741.             BEGIN
  2742.             fIsSelected := TRUE;
  2743.             fWasSelected := TRUE;
  2744.             {$Push} {$h-}
  2745.             OffSetRect(fExtentRect, whereToPaste.h, whereToPaste.v);
  2746.             {$Pop}
  2747.             END;
  2748.         fPasteList.InsertLast(aShape);
  2749.         fShapeView.InvalShape(aShape);
  2750.         END;
  2751.  
  2752.     BEGIN
  2753.     {$IFC qDebug}
  2754.     IF NOT Member(gClipView, TShapeView) THEN
  2755.         ProgramBreak('Attempt to paste a non-TShapeView clipboard');
  2756.     {$ENDC}
  2757.  
  2758.  { The next section figures out where the pasted shapes should be placed
  2759.    in the view. Lovely, isn't it? }
  2760.     IF gPasteReplacesSelection THEN
  2761.         BEGIN
  2762.         { If we're replacing shapes, then paste the new shapes starting at
  2763.           the top-left corner of the replaced shapes. Otherwise, start
  2764.           at the last clicked point in the view }
  2765.         fShapeDocument.SurveyShapes(TRUE, noOfShapes, extent);
  2766.         IF noOfShapes > 0 THEN
  2767.             whereToPaste := extent.topLeft
  2768.         ELSE
  2769.             whereToPaste := fShapeView.fClickPt;
  2770.         END
  2771.     ELSE
  2772.         BEGIN
  2773.         fShapeView.fSuperView.GetExtent(scrollerExtent);
  2774.         FOR vhs := v TO h DO
  2775.             WITH scrollerExtent.topLeft DO
  2776.                 BEGIN
  2777.                 { temp var "t" needed because Code Generator finds the
  2778.                   following expression too complex }
  2779.                 t := (scrollerExtent.botRight.vh[vhs] + {scrollerExtent.} vh[vhs] -
  2780.                      gClipView.fSize.vh[vhs]) DIV 2;
  2781.                 whereToPaste.vh[vhs] := Max(            {translation.} vh[vhs], t);
  2782.                 END;
  2783.         END;
  2784.     SubPt(gClipMargin, whereToPaste);
  2785.  
  2786.     fShapeView.SaveSelection(gPasteReplacesSelection);
  2787.     fShapeView.Deselect;
  2788.  
  2789.     TShapeView(gClipView).fShapeDocument.EachShapeDo(PasteShape);
  2790.  
  2791.     fShapeDocument.fFiltering := gPasteReplacesSelection;
  2792.     fShapeDocument.fReplaceCommand := SELF;
  2793.     fShapeView.AdjustSize;                                {Make sure all the Pasted shapes can be
  2794.                                                          seen}
  2795.     END;
  2796.  
  2797. {--------------------------------------------------------------------------------------------------}
  2798. {$S ADoCommand}
  2799.  
  2800. PROCEDURE TShapePasteCommand.Free; OVERRIDE;
  2801.  
  2802.     BEGIN
  2803.     fPasteList := FreeListIfObject(fPasteList);         {Free the TList object itself}
  2804.  
  2805.     INHERITED Free;
  2806.     END;
  2807.  
  2808. {--------------------------------------------------------------------------------------------------}
  2809. {$S ARes}
  2810.  
  2811. PROCEDURE TShapePasteCommand.EachNewShapeDo(PROCEDURE
  2812.                                             DoThis(shape: TShape));
  2813.  
  2814.     BEGIN
  2815.     fPasteList.Each(DoThis);
  2816.     END;
  2817.  
  2818. {--------------------------------------------------------------------------------------------------}
  2819. {$ARes}
  2820.  
  2821. FUNCTION TShapePasteCommand.FirstShapeThat(FUNCTION
  2822.                                            TestShape(aShape: TShape): BOOLEAN): TShape;
  2823.  
  2824.     BEGIN
  2825.     FirstShapeThat := TShape(fPasteList.FirstThat(TestShape));
  2826.     END;
  2827.  
  2828. {--------------------------------------------------------------------------------------------------}
  2829. {$S ADoCommand}
  2830.  
  2831. PROCEDURE TShapePasteCommand.UndoIt; OVERRIDE;
  2832.  
  2833. {--------------------------------------------------------------------------------------------------}
  2834.  
  2835.     PROCEDURE BeInvalidated(theShape: TShape);
  2836.  
  2837.         BEGIN
  2838.         fShapeView.InvalShape(theShape);
  2839.         END;
  2840.  
  2841.     BEGIN
  2842.     INHERITED UndoIt;
  2843.     EachNewShapeDo(BeInvalidated);
  2844.     IF fShapeView.Focus THEN;
  2845.     fShapeDocument.fFiltering := FALSE;
  2846.     fShapeView.AdjustSize;                                {In case we replaced shapes when we Pasted}
  2847.     END;
  2848.  
  2849. {--------------------------------------------------------------------------------------------------}
  2850. {$S ADoCommand}
  2851.  
  2852. PROCEDURE TShapePasteCommand.RedoIt; OVERRIDE;
  2853.  
  2854.     PROCEDURE BeSelected(shape: TShape);
  2855.  
  2856.         BEGIN
  2857.         shape.fIsSelected := TRUE;
  2858.         fShapeView.InvalShape(shape);
  2859.         END;
  2860.  
  2861.     BEGIN
  2862.     IF fShapeView.Focus THEN;
  2863.     fShapeView.Deselect;
  2864.     EachNewShapeDo(BeSelected);                         {Invalidate all the newly-added shapes}
  2865.     fShapeDocument.fFiltering := gPasteReplacesSelection;
  2866.     INHERITED RedoIt;
  2867.     fShapeView.AdjustSize;                                {Make sure size reflects the Paste}
  2868.     END;
  2869.  
  2870. {--------------------------------------------------------------------------------------------------}
  2871. {$S ASelCommand}
  2872.  
  2873. PROCEDURE TShapeClearCommand.IShapeClearCommand(itsShapeView: TShapeView);
  2874.  
  2875.     BEGIN
  2876.     IShapeCommand(cClear, itsShapeView, NOT kBetterFeedbackDesired);
  2877.     END;
  2878.  
  2879. {--------------------------------------------------------------------------------------------------}
  2880. {$S ADoCommand}
  2881.  
  2882. PROCEDURE TShapeClearCommand.DoIt;
  2883.  
  2884.     BEGIN
  2885.     fShapeView.SaveSelection(TRUE);                     {TRUE means invalidate the shapes}
  2886.     fShapeDocument.fFiltering := TRUE;
  2887.     fShapeDocument.fReplaceCommand := SELF;
  2888.     END;
  2889.  
  2890. {--------------------------------------------------------------------------------------------------}
  2891. {$S ADoCommand}
  2892.  
  2893. PROCEDURE TShapeClearCommand.RedoIt;
  2894.  
  2895.     BEGIN
  2896.     fShapeDocument.fFiltering := TRUE;
  2897.     INHERITED RedoIt;
  2898.     END;
  2899.  
  2900. {--------------------------------------------------------------------------------------------------}
  2901. {--------------------------------------------------------------------------------------------------}
  2902. {--------------------------------------------------------------------------------------------------}
  2903.  
  2904. {--------------------------------------------------------------------------------------------------}
  2905. {$S AInit}
  2906.  
  2907. PROCEDURE TShadeMenu.IShadeMenu;
  2908.  
  2909.     VAR
  2910.         i, j:                INTEGER;
  2911.         r:                    Rect;
  2912.         ChoiceID:            INTEGER;
  2913.  
  2914.     BEGIN
  2915.     IMenu(kShadesMenu                                    {resource ID} , kWShadeChoice + 2,
  2916.           kShadeTop + kHShadeChoice + 2);
  2917.  
  2918.     { Create the rectangles that hold the shades}
  2919.     SetRect(r, kShadeLeft, kShadeTop, kWShadeCell + kShadeLeft, kHShadeCell + kShadeTop);
  2920.  
  2921.     ChoiceID := 1;
  2922.     {Set the Shade choice rectangles that we can select from}
  2923.     FOR i := 1 TO kShadesDown DO
  2924.         BEGIN
  2925.         FOR j := 1 TO kShadesAcross DO                    {all the rectangles across}
  2926.             BEGIN
  2927.             fChoiceArray[ChoiceID] := r;
  2928.             OffSetRect(r, kWShadeCell + kWCellSpace, 0);
  2929.             ChoiceID := ChoiceID + 1;
  2930.             END;
  2931.         { move rectangle down one row }
  2932.         SetRect(r, kShadeLeft, r.bottom + kHCellSpace, kWShadeCell + kShadeLeft, r.bottom +
  2933.                 kHShadeCell + kHCellSpace);
  2934.         END;
  2935.  
  2936.     { For this simple example, hard code in the command number to correspond to
  2937.       menu position. }
  2938.     fShadeCommands[1] := - cWhite;
  2939.     fShadeCommands[2] := - cLtGray;
  2940.     fShadeCommands[3] := - cGray;
  2941.     fShadeCommands[4] := - cDkGray;
  2942.     fShadeCommands[5] := - cBlack;
  2943.     fShadeCommands[6] := kNoMenuItem;
  2944.     END;
  2945.  
  2946. {--------------------------------------------------------------------------------------------------}
  2947. {$S ARes}
  2948.  
  2949. PROCEDURE TShadeMenu.Draw(area: Rect);
  2950.  
  2951.     VAR
  2952.         i:                    INTEGER;
  2953.         r:                    Rect;
  2954.         theMenuColors:        MenuColors;
  2955.  
  2956.     BEGIN
  2957.     { Use the choice array we created to draw the shades (or custom labels) in.}
  2958.     FOR i := 1 TO Min(kShadesDown * kShadesAcross, kNoOfShades) DO
  2959.         BEGIN
  2960.  
  2961.         { set the correct colors for the item }
  2962.         GetMenuColors(fMenuHandle^^.menuID, i, theMenuColors);
  2963.         SetIfColor(theMenuColors.itemColor);
  2964.         SetIfBkColor(theMenuColors.backGroundColor);
  2965.  
  2966.         r := fChoiceArray[i];
  2967.         FrameRect(r);
  2968.         InsetRect(r, 2, 2);
  2969.         FillRect(r, gPat[cShadeBase + (i - 1)]);
  2970.         END;
  2971.  
  2972.     INHERITED Draw(area);
  2973.     END;
  2974.  
  2975. {--------------------------------------------------------------------------------------------------}
  2976. {$S ARes}
  2977.  
  2978. FUNCTION TShadeMenu.FindItem(hitPt: Point): INTEGER; OVERRIDE;
  2979.  
  2980.     VAR
  2981.         i:                    INTEGER;
  2982.         r:                    Rect;
  2983.  
  2984.     BEGIN
  2985.     FindItem := kNoMenuItem;                            { return noSelection Made as default }
  2986.     FOR i := 1 TO kShadesDown * kShadesAcross DO
  2987.         BEGIN
  2988.         r := fChoiceArray[i];
  2989.         IF PtInRect(hitPt, r) THEN
  2990.             BEGIN
  2991.             FindItem := fShadeCommands[i];
  2992.             LEAVE;
  2993.             END;
  2994.         END;
  2995.     END;
  2996.  
  2997. {--------------------------------------------------------------------------------------------------}
  2998. {$S ARes}
  2999.  
  3000. PROCEDURE TShadeMenu.Highlight(whichItem: INTEGER; turnItOn: BOOLEAN);
  3001.  
  3002.     VAR
  3003.         i:                    INTEGER;
  3004.         r:                    Rect;
  3005.         theMenuColors:        MenuColors;
  3006.  
  3007.     BEGIN
  3008.     FOR i := 1 TO kShadesDown * kShadesAcross DO
  3009.         BEGIN
  3010.         IF fShadeCommands[i] = whichItem THEN
  3011.             BEGIN
  3012.             r := fChoiceArray[i];
  3013.  
  3014.             { set the correct colors for the item }
  3015.             GetMenuColors(fMenuHandle^^.menuID, i, theMenuColors);
  3016.             SetIfColor(theMenuColors.itemColor);
  3017.             SetIfBkColor(theMenuColors.backGroundColor);
  3018.  
  3019.             { Normal hilight by reversing foreground and background. and redrawing.
  3020.             See IM-V pp. 235-236
  3021.  
  3022.             In this case the user is choosing a pattern so it is better to just
  3023.             frame the pattern }
  3024.  
  3025.             IF NOT turnItOn THEN
  3026.                 BEGIN
  3027.                 SetIfBkColor(theMenuColors.itemColor);
  3028.                 SetIfColor(theMenuColors.backGroundColor);
  3029.                 END
  3030.             ELSE
  3031.                 BEGIN
  3032.                 SetIfColor(theMenuColors.itemColor);
  3033.                 SetIfBkColor(theMenuColors.backGroundColor);
  3034.                 END;
  3035.  
  3036.             InsetRect(r, - 1, - 1);
  3037.             FrameRect(r);
  3038.             InsetRect(r, 2, 2);
  3039.             FrameRect(r);
  3040.             LEAVE;
  3041.             END;
  3042.         END;
  3043.     END;
  3044.